Merge plugin experimentation branch - GstBaseSrcs can now be implemented via the bindings but with very limited functionality still

This commit is contained in:
Avi Zimmerman
2021-01-06 22:50:03 +02:00
parent 7f4972677d
commit 26cf65f211
34 changed files with 1950 additions and 919 deletions

View File

@@ -1,89 +0,0 @@
package main
import (
"bytes"
"fmt"
"io"
"regexp"
"strings"
"text/tabwriter"
)
type color string
var (
colorReset color = "\033[0m"
colorBlack color = "\033[0;30m"
colorRed color = "\033[0;31m"
colorGreen color = "\033[0;32m"
colorOrange color = "\033[0;33m"
colorBlue color = "\033[0;34m"
colorPurple color = "\033[0;35m"
colorCyan color = "\033[0;36m"
colorLightGray color = "\033[0;37m"
colorDarkGray color = "\033[1;30m"
colorLightRed color = "\033[1;31m"
colorLightGreen color = "\033[1;32m"
colorYellow color = "\033[1;33m"
colorLightBlue color = "\033[1;34m"
colorLightPurple color = "\033[1;35m"
colorLightCyan color = "\033[1;36m"
colorWhite color = "\033[1;37m"
)
func disableColor() {
colorReset = ""
colorBlack = ""
colorRed = ""
colorGreen = ""
colorOrange = ""
colorBlue = ""
colorPurple = ""
colorCyan = ""
colorLightGray = ""
colorDarkGray = ""
colorLightRed = ""
colorLightGreen = ""
colorYellow = ""
colorLightBlue = ""
colorLightPurple = ""
colorLightCyan = ""
colorWhite = ""
}
func (c color) print(s string) { fmt.Printf("%s%s%s", c, s, colorReset) }
func (c color) sprint(s string) string { return fmt.Sprintf("%s%s%s", c, s, colorReset) }
func (c color) printIndent(i int, s string) { c.print(fmt.Sprintf("%s%s", strings.Repeat(" ", i), s)) }
func (c color) printf(f string, args ...interface{}) { c.print(fmt.Sprintf(f, args...)) }
func (c color) printfIndent(i int, f string, args ...interface{}) {
c.printf(fmt.Sprintf("%s%s", strings.Repeat(" ", i), fmt.Sprintf(f, args...)))
}
func (c color) fprint(w io.Writer, s string) { fmt.Fprintf(w, fmt.Sprintf("%s%s%s", c, s, colorReset)) }
func (c color) fprintf(w io.Writer, f string, args ...interface{}) {
c.fprint(w, fmt.Sprintf(f, args...))
}
func (c color) fprintIndent(w io.Writer, i int, s string) {
c.fprint(w, fmt.Sprintf("%s%s", strings.Repeat(" ", i), s))
}
func (c color) fprintfIndent(w io.Writer, i int, f string, args ...interface{}) {
c.fprint(w, fmt.Sprintf("%s%s", strings.Repeat(" ", i), fmt.Sprintf(f, args...)))
}
var srcRegex = regexp.MustCompile("^\\[.*?\\]")
var typeRegex = regexp.MustCompile("[A-Z\\-]+")
var msgRegex = regexp.MustCompile("[^\\-]+$")
// colorify adds color to a string in the format of "[src] TYPE - MESSAGE"
func colorify(str string) string {
buf := new(bytes.Buffer)
w := new(tabwriter.Writer)
w.Init(buf, 4, 24, 0, '\t', 0)
fmt.Fprintf(w,
"%s \t %s \t %s",
colorBlue.sprint(srcRegex.FindString(str)),
colorGreen.sprint(typeRegex.FindString(str)),
colorLightGray.sprint(msgRegex.FindString(str)),
)
w.Flush()
return buf.String()
}

View File

@@ -1,304 +0,0 @@
// Inspect is a simplified version of gst-inspect-<version>. It parses
// the name of a plugin on the command line and dumps the element's properties
// to stdout.
package main
import (
"bytes"
"fmt"
"os"
"strings"
"text/tabwriter"
"github.com/gotk3/gotk3/glib"
"github.com/tinyzimmer/go-gst/gst"
)
func main() {
if len(os.Args) == 1 {
fmt.Println("You must provide an element to inspect")
os.Exit(1)
}
gst.Init(nil)
if err := inspect(os.Args[1]); err != nil {
fmt.Println(err)
os.Exit(2)
}
}
func inspect(name string) error {
// load the registry
registry := gst.GetRegistry()
// get the factory for the element
factory := gst.Find(name)
if factory == nil {
return fmt.Errorf("Could not get details for factory '%s'", name)
}
defer factory.Unref()
// assume it's an element for now, can implement more later
elem, err := gst.NewElement(name)
if err != nil {
return err
}
defer elem.Unref()
var maxLevel int
// dump info about the element
printFactoryDetails(registry, factory)
printPluginDetails(registry, factory)
printHierarchy(elem.TypeFromInstance(), 0, &maxLevel)
printInterfaces(elem)
printPadTemplates(elem)
printClockingInfo(elem)
printURIHandlerInfo(elem)
printPadInfo(elem)
printElementPropertiesInfo(elem)
printSignalInfo(elem)
printChildrenInfo(elem)
printPresentList(elem)
return nil
}
func printFactoryDetails(registry *gst.Registry, factory *gst.ElementFactory) {
// initialize tabwriter
w := new(tabwriter.Writer)
buf := new(bytes.Buffer)
w.Init(
buf,
40, // minwidth
30, // tabwith
0, // padding
' ', // padchar
0, // flags
)
colorOrange.fprint(w, "Factory Details:\n")
for _, key := range factory.GetMetadataKeys() {
colorBlue.fprintfIndent(w, 2, "%s \t ", strings.Title(key))
colorLightGray.fprint(w, factory.GetMetadata(key))
colorReset.fprint(w, "\n")
}
w.Flush()
fmt.Print(buf.String())
fmt.Println()
}
func printPluginDetails(registry *gst.Registry, factory *gst.ElementFactory) {
// initialize tabwriter
w := new(tabwriter.Writer)
buf := new(bytes.Buffer)
w.Init(
buf,
40, // minwidth
30, // tabwith
0, // padding
' ', // padchar
0, // flags
)
pluginFeature, err := registry.LookupFeature(factory.Name())
if err != nil {
return
}
plugin := pluginFeature.GetPlugin()
if plugin == nil {
return
}
defer pluginFeature.Unref()
defer plugin.Unref()
colorOrange.fprint(w, "Plugin Details:\n")
colorBlue.fprintIndent(w, 2, "Name \t ")
colorLightGray.fprintf(w, "%s\n", pluginFeature.GetPluginName())
colorBlue.fprintIndent(w, 2, "Description \t ")
colorLightGray.fprintf(w, "%s\n", plugin.Description())
colorBlue.fprintIndent(w, 2, "Filename \t ")
colorLightGray.fprintf(w, "%s\n", plugin.Filename())
colorBlue.fprintIndent(w, 2, "Version \t ")
colorLightGray.fprintf(w, "%s\n", plugin.Version())
colorBlue.fprintIndent(w, 2, "License \t ")
colorLightGray.fprintf(w, "%s\n", plugin.License())
colorBlue.fprintIndent(w, 2, "Source module \t ")
colorLightGray.fprintf(w, "%s\n", plugin.Source())
colorBlue.fprintIndent(w, 2, "Binary package \t ")
colorLightGray.fprintf(w, "%s\n", plugin.Package())
colorBlue.fprintIndent(w, 2, "Origin URLs \t ")
colorLightGray.fprintf(w, "%s\n", plugin.Origin())
w.Flush()
fmt.Print(buf.String())
fmt.Println()
}
func printHierarchy(gtype glib.Type, level int, maxLevel *int) {
parent := gtype.Parent()
*maxLevel = *maxLevel + 1
level++
if parent > 0 {
printHierarchy(parent, level, maxLevel)
}
for i := 1; i < *maxLevel-level; i++ {
colorReset.print(" ")
}
if *maxLevel-level > 0 {
colorLightPurple.print(" +----")
}
colorGreen.printf("%s\n", gtype.Name())
}
func printInterfaces(elem *gst.Element) {
fmt.Println()
if ifaces := elem.Interfaces(); len(ifaces) > 0 {
colorOrange.print("Implemented Interfaces:")
for _, iface := range ifaces {
colorGreen.printfIndent(2, "%s\n", iface)
}
}
}
func printPadTemplates(elem *gst.Element) {
fmt.Println()
tmpls := elem.GetPadTemplates()
if len(tmpls) == 0 {
return
}
colorOrange.print("Pad templates:\n")
for _, tmpl := range tmpls {
colorBlue.printfIndent(2, "%s template", strings.ToUpper(tmpl.Name()))
colorReset.print(": ")
colorBlue.printf("'%s'\n", strings.ToLower(tmpl.Direction().String()))
colorBlue.printIndent(4, "Availability")
colorReset.print(": ")
colorLightGray.print(strings.Title(tmpl.Presence().String()))
colorReset.print("\n")
colorBlue.printIndent(4, "Capabilities")
colorReset.print(": ")
printCaps(tmpl.Caps(), 6)
}
fmt.Println()
fmt.Println()
}
func printClockingInfo(elem *gst.Element) {
if !elem.Has(gst.ElementFlagRequireClock) && !elem.Has(gst.ElementFlagProvideClock) {
colorLightGray.print("Element has no clocking capabilities.\n")
return
}
fmt.Printf("%sClocking Interactions:%s\n", colorOrange, colorReset)
if elem.Has(gst.ElementFlagRequireClock) {
colorLightGray.printIndent(2, "element requires a clock\n")
}
if elem.Has(gst.ElementFlagProvideClock) {
clock := elem.GetClock()
if clock == nil {
colorLightGray.printIndent(2, "element is supposed to provide a clock but returned NULL%s\n")
} else {
defer clock.Unref()
colorLightGray.printIndent(2, "element provides a clock: ")
colorCyan.printf(clock.Name())
}
}
fmt.Println()
}
func printURIHandlerInfo(elem *gst.Element) {
if !elem.IsURIHandler() {
colorLightGray.print("Element has no URI handling capabilities.\n")
fmt.Println()
}
uriHandler := elem.URIHandler()
colorOrange.print("URI handling capabilities:\n")
colorLightGray.printfIndent(2, "Element can act as %s.\n", strings.ToLower(uriHandler.GetURIType().String()))
protos := uriHandler.GetURIProtocols()
if len(protos) == 0 {
fmt.Println()
return
}
colorLightGray.printIndent(2, "Supported URI protocols:\n")
for _, proto := range protos {
colorCyan.printfIndent(4, "%s\n", proto)
}
fmt.Println()
}
func printPadInfo(elem *gst.Element) {
colorOrange.print("Pads:\n")
pads := elem.GetPads()
if len(pads) == 0 {
colorCyan.printIndent(2, "none\n")
return
}
for _, pad := range elem.GetPads() {
defer pad.Unref()
colorBlue.printIndent(2, strings.ToUpper(pad.Direction().String()))
colorReset.print(": ")
colorLightGray.printf("'%s'\n", pad.Name())
if tmpl := pad.Template(); tmpl != nil {
defer tmpl.Unref()
colorBlue.printIndent(4, "Pad Template")
colorReset.print(": ")
colorLightGray.printf("'%s'\n", tmpl.Name())
}
if caps := pad.CurrentCaps(); caps != nil {
colorBlue.printIndent(2, "Capabilities:\n")
printCaps(caps, 4)
}
}
fmt.Println()
}
func printElementPropertiesInfo(elem *gst.Element) {
printObjectPropertiesInfo(elem.Object, "Element Properties")
}
func printSignalInfo(elem *gst.Element) {}
func printChildrenInfo(elem *gst.Element) {}
func printPresentList(elem *gst.Element) {}

View File

@@ -1,349 +0,0 @@
package main
import (
"bytes"
"fmt"
"sort"
"strconv"
"text/tabwriter"
"github.com/gotk3/gotk3/glib"
"github.com/tinyzimmer/go-gst/gst"
)
func printFieldType(s string) {
colorGreen.printIndent(24, s)
}
func printFieldName(s string) {
colorOrange.print(s)
colorReset.print(": ")
}
func printFieldValue(s string) {
colorCyan.printf("%s ", s)
}
// ByName implements sort. Interface based on the Name field.
type ByName []*gst.ParameterSpec
func (a ByName) Len() int { return len(a) }
func (a ByName) Less(i, j int) bool { return a[i].Name < a[j].Name }
func (a ByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// ByValue implements sort. Interface based on the Value field.
type ByValue []*gst.FlagsValue
func (a ByValue) Len() int { return len(a) }
func (a ByValue) Less(i, j int) bool { return a[i].Value < a[j].Value }
func (a ByValue) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func printObjectPropertiesInfo(obj *gst.Object, description string) {
colorOrange.printf("%s:\n", description)
// for now this function only handles elements
props := obj.ListProperties()
sort.Sort(ByName(props))
for _, param := range props {
defer param.Unref()
colorBlue.printfIndent(2, "%-20s", param.Name)
colorReset.printf(": %s", param.Blurb)
colorReset.print("\n")
colorOrange.printIndent(24, "flags")
colorReset.print(": ")
colorCyan.print(param.Flags.GstFlagsString())
if !param.Flags.Has(gst.ParameterReadable) {
colorReset.print(" | ")
colorLightPurple.print("Write only")
} else if !param.Flags.Has(gst.ParameterWritable) {
colorReset.print(" | ")
colorLightPurple.print("Read only")
}
colorReset.print("\n")
// get the value and continue on any error (very unlikely)
goval, _ := param.DefaultValue.GoValue()
// skips deprecated types
switch param.ValueType {
case glib.TYPE_STRING:
printFieldType("String. ")
printFieldName("Default")
if goval == nil {
printFieldValue("null")
} else {
str, _ := param.DefaultValue.GetString()
printFieldValue(str)
}
case glib.TYPE_BOOLEAN:
var valStr string
if goval == nil {
valStr = "unknown" // edge case
} else {
b := goval.(bool)
valStr = fmt.Sprintf("%t", b)
}
printFieldType("Boolean. ")
printFieldName("Default")
printFieldValue(valStr)
case glib.TYPE_UINT:
var valStr string
if goval == nil {
valStr = "0"
} else {
v := goval.(uint)
valStr = fmt.Sprintf("%d", v)
}
printFieldType("Unsigned Integer. ")
printFieldName("Range")
min, max := param.UIntRange()
printFieldValue(fmt.Sprintf("%d - %d ", min, max))
printFieldName("Default")
printFieldValue(valStr)
case glib.TYPE_INT:
var valStr string
if goval == nil {
valStr = "0"
} else {
v := goval.(int)
valStr = fmt.Sprintf("%d", v)
}
printFieldType("Integer. ")
printFieldName("Range")
min, max := param.IntRange()
printFieldValue(fmt.Sprintf("%d - %d ", min, max))
printFieldName("Default")
printFieldValue(valStr)
case glib.TYPE_UINT64:
var valStr string
if goval == nil {
valStr = "0"
} else {
v := goval.(uint64)
valStr = fmt.Sprintf("%d", v)
}
printFieldType("Unsigned Integer64. ")
printFieldName("Range")
min, max := param.UInt64Range()
printFieldValue(fmt.Sprintf("%d - %d ", min, max))
printFieldName("Default")
printFieldValue(valStr)
case glib.TYPE_INT64:
var valStr string
if goval == nil {
valStr = "0"
} else {
v := goval.(int64)
valStr = fmt.Sprintf("%d", v)
}
printFieldType("Integer64. ")
printFieldName("Range")
min, max := param.Int64Range()
printFieldValue(fmt.Sprintf("%d - %d ", min, max))
printFieldName("Default")
printFieldValue(valStr)
case glib.TYPE_FLOAT:
var valStr string
if goval == nil {
valStr = "0"
} else {
v := goval.(float64)
valStr = fmt.Sprintf("%15.7g", v)
}
printFieldType("Float. ")
printFieldName("Range")
min, max := param.FloatRange()
printFieldValue(fmt.Sprintf("%15.7g - %15.7g ", min, max))
printFieldName("Default")
printFieldValue(valStr)
case glib.TYPE_DOUBLE:
var valStr string
if goval == nil {
valStr = "0"
} else {
v := goval.(float64)
valStr = fmt.Sprintf("%15.7g", v)
}
printFieldType("Double. ")
printFieldName("Range")
min, max := param.DoubleRange()
printFieldValue(fmt.Sprintf("%15.7g - %15.7g ", min, max))
printFieldName("Default")
printFieldValue(valStr)
default:
if param.IsCaps() {
if caps := param.GetCaps(); caps != nil {
printCaps(caps, 24)
}
} else if param.IsEnum() {
enumValues := param.GetEnumValues()
iface, _ := param.DefaultValue.GoValue()
var curVal string
if iface == nil {
curVal = "-1"
} else {
curVal = fmt.Sprintf("%v", iface)
}
var defaultStr string
for _, val := range enumValues {
if curVal == strconv.Itoa(val.Value) {
defaultStr = val.ValueNick
}
}
printFieldType(fmt.Sprintf(`Enum "%s" `, param.ValueType.Name()))
printFieldName("Default")
printFieldValue(fmt.Sprintf(`%s "%s"`, curVal, defaultStr))
colorReset.print("\n")
for idx, val := range enumValues {
w := new(tabwriter.Writer)
buf := new(bytes.Buffer)
w.Init(buf, 100, 73, 0, '\t', 0)
colorOrange.fprintfIndent(w, 27, "(%d)", val.Value)
colorReset.fprint(w, ": ")
colorCyan.fprint(w, val.ValueNick)
colorReset.fprint(w, "\t - ")
colorLightGray.fprint(w, val.ValueName)
w.Flush()
fmt.Print(buf.String())
if idx < len(enumValues)-1 {
colorReset.print("\n")
}
}
} else if param.IsFlags() {
flags := param.GetFlagValues()
sort.Sort(ByValue(flags))
flagStr := "+"
for _, flag := range flags {
flagStr += fmt.Sprintf(" %s", flag.ValueNick)
}
if flagStr == "+" {
flagStr = "(none)"
}
printFieldType(fmt.Sprintf(`Flags "%s" `, param.ValueType.Name()))
printFieldName("Default")
printFieldValue(fmt.Sprintf(`0x%08x "%s"`, param.GetDefaultFlags(), flagStr))
for idx, flag := range flags {
w := new(tabwriter.Writer)
buf := new(bytes.Buffer)
w.Init(buf, 100, 73, 0, '\t', 0)
colorOrange.fprintfIndent(w, 27, "(%d)", flag.Value)
colorReset.fprint(w, ": ")
colorCyan.fprint(w, flag.ValueNick)
colorReset.fprint(w, "\t - ")
colorLightGray.fprint(w, flag.ValueName)
w.Flush()
fmt.Print(buf.String())
if idx < len(flags)-1 {
colorReset.print("\n")
}
}
} else if param.IsObject() {
colorLightGray.printIndent(24, "Object of type ")
colorGreen.printf(`"%s"`, param.ValueType.Name())
} else if param.IsBoxed() {
colorLightGray.printIndent(24, "Boxed pointer of type ")
colorGreen.printf(`"%s"`, param.ValueType.Name())
if param.ValueType.Name() == "GstStructure" {
structure := gst.StructureFromGValue(param.DefaultValue)
if structure != nil {
for key, val := range structure.Values() {
colorReset.printIndent(26, "(gpointer) ")
colorYellow.printf("%15s:", key)
colorBlue.printf("%v", val)
}
}
}
} else if param.IsPointer() {
colorLightGray.printIndent(24, "Pointer of type ")
colorGreen.printf(`"%s`, param.ValueType.Name())
} else if param.IsFraction() {
colorGreen.printIndent(24, "Fraction.")
} else if param.IsGstArray() {
colorGreen.printIndent(24, "GstArray.")
} else {
colorReset.printIndent(24, "Unknown type ")
colorGreen.printf(`"%s`, param.ValueType.Name())
}
}
colorReset.print("\n")
}
fmt.Println()
}
func printCaps(caps *gst.Caps, indent int) {
if caps == nil {
return
}
colorReset.print("\n")
defer func() { colorReset.print("\n") }()
if caps.IsAny() {
colorOrange.printIndent(indent, "ANY")
return
}
if caps.IsEmpty() {
colorOrange.printIndent(indent, "EMPTY")
return
}
for i := 0; i < caps.GetSize(); i++ {
structure := caps.GetStructureAt(i)
features := caps.GetFeaturesAt(i)
if features != nil && features.IsAny() {
colorOrange.printIndent(indent+20, structure.Name())
colorLightGray.print(" (")
colorGreen.print(features.String())
colorLightGray.print(")")
} else {
colorOrange.printIndent(indent+20, structure.Name())
}
colorReset.print("\n")
for k, v := range structure.Values() {
colorCyan.printIndent(indent+10, k)
colorReset.print(": ")
colorBlue.printf("%v\n", v)
}
}
}

View File

@@ -0,0 +1,325 @@
// This example demonstrates a filesrc plugin implemented in Go.
//
// Every element in a Gstreamer pipeline is provided by plugins. Some are builtin while
// others are provided by third-parties or distributed privately. The plugins are built
// around the GObject type system.
//
// Go-gst offers loose bindings around the GObject type system to provide the necessary
// functionality to implement these plugins. The example in this code produces an element
// that can read from a file on the local system.
//
// In order to build the plugin for use by GStreamer, you may do the following:
//
// $ go build -o libgstgofilesrc.so -buildmode c-shared .
//
//
package main
import (
"errors"
"fmt"
"io"
"os"
"strings"
"syscall"
"github.com/gotk3/gotk3/glib"
"github.com/tinyzimmer/go-gst/gst"
"github.com/tinyzimmer/go-gst/gst/base"
)
// Here we define a list of ParamSpecs that will make up the properties for our element.
// This element only has a single property, the location of the file to read from.
// When getting and setting properties later on, you will reference them by their index in
// this list.
var properties = []*gst.ParameterSpec{
gst.NewStringParameter(
"location", // The name of the parameter
"File Location", // The long name for the parameter
"Location of the file to read from", // A blurb about the parameter
nil, // A default value for the parameter
gst.ParameterReadWrite, // Flags for the parameter
),
}
// Here we declare a private struct to hold our internal state.
type state struct {
// Whether the element is started or not
started bool
// The file the element is reading from
file *os.File
// The current position in the file
position uint64
}
// This is another private struct where we hold the parameter values set on our
// element.
type settings struct {
location string
}
// Finally a structure is defined that implements (at a minimum) the gst.GoElement interface.
// It is possible to signal to the bindings to inherit from other classes or implement other
// interfaces via the registration and TypeInit processes.
type fileSrc struct {
// The settings for the element
settings *settings
// The current state of the element
state *state
}
// Private methods only used internally by the plugin
// setLocation is a simple method to check the validity of a provided file path and set the
// local value with it.
func (f *fileSrc) setLocation(path string) error {
if f.state.started {
return errors.New("Changing the `location` property on a started `GoFileSrc` is not supported")
}
path = strings.TrimPrefix(path, "file://")
if path == "" {
f.settings.location = ""
return nil
}
stat, err := os.Stat(path)
if err != nil {
if os.IsNotExist(err) {
return fmt.Errorf("%s does not exist", path)
}
return fmt.Errorf("Could not stat %s, err: %s", path, err.Error())
}
if stat.IsDir() {
return fmt.Errorf("%s is a directory", path)
}
f.settings.location = path
return nil
}
// The ObjectSubclass implementations below are for registering the various aspects of our
// element and its capabilities with the type system.
// Every element needs to provide its own constructor that returns the an initialized
// gst.GoElement implementation. Here we simply create a new fileSrc with zeroed settings
// and state objects.
func (f *fileSrc) New() gst.GoElement {
return &fileSrc{
settings: &settings{},
state: &state{},
}
}
// The TypeInit method should register any additional interfaces provided by the element.
// In this example we signal to the type system that we also implement the GstURIHandler interface.
func (f *fileSrc) TypeInit(instance *gst.TypeInstance) {
instance.AddInterface(gst.InterfaceURIHandler)
}
// The ClassInit method should specify the metadata for this element as well as add any pad templates
// and properties.
func (f *fileSrc) ClassInit(klass *gst.ElementClass) {
klass.SetMetadata(
"File Source",
"Source/File",
"Read stream from a file",
"Avi Zimmerman <avi.zimmerman@gmail.com>",
)
caps := gst.NewAnyCaps()
srcPadTemplate := gst.NewPadTemplate(
"src",
gst.PadDirectionSource,
gst.PadPresenceAlways,
caps,
)
klass.AddPadTemplate(srcPadTemplate)
klass.InstallProperties(properties)
}
// Object implementations are used during the initialization of element. The
// methods are called once the obejct is constructed and its properties are read
// and written to.
// SetProperty is called when a `value` is set to the property at index `id` in the
// properties slice that we installed during ClassInit. It should attempt to register
// the value locally or signal any errors that occur in the process.
func (f *fileSrc) SetProperty(self *gst.Object, id uint, value *glib.Value) {
param := properties[id]
switch param.Name() {
case "location":
var val string
if value == nil {
val = ""
} else {
val, _ = value.GetString()
}
if err := f.setLocation(val); err != nil {
gst.ToElement(self).Error(gst.DomainLibrary, gst.LibraryErrorSettings,
"Could not set location on object",
err.Error(),
)
}
gst.ToElement(self).Info(gst.DomainLibrary, fmt.Sprintf("Set location to %s", f.settings.location))
}
}
// GetProperty is called to retrieve the value of the property at index `id` in the properties
// slice provided at ClassInit.
func (f *fileSrc) GetProperty(self *gst.Object, id uint) *glib.Value {
param := properties[id]
switch param.Name() {
case "location":
if f.settings.location == "" {
return nil
}
val, err := glib.GValue(f.settings.location)
if err == nil {
return val
}
gst.ToElement(self).Error(gst.DomainLibrary, gst.LibraryErrorSettings,
fmt.Sprintf("Could not convert %s to GValue", f.settings.location),
err.Error(),
)
}
return nil
}
// Constructed is called when the type system is done constructing the object. Any finalizations required
// during the initializatin cycle can be performed here. In this example, we set the format on our
// underlying GstBaseSrc to bytes.
func (f *fileSrc) Constructed(self *gst.Object) {
base.ToGstBaseSrc(self).SetFormat(gst.FormatBytes)
}
// GstBaseSrc implementations are optional methods to implement from the base.GstBaseSrcImpl interface.
// If the method is not overridden by the implementing struct, it will be inherited from the parent class.
// IsSeekable returns that we are, in fact, seekable.
func (f *fileSrc) IsSeekable(*base.GstBaseSrc) bool { return true }
// GetSize will return the total size of the file at the configured location.
func (f *fileSrc) GetSize(self *base.GstBaseSrc) (bool, int64) {
if !f.state.started {
return false, 0
}
stat, err := f.state.file.Stat()
if err != nil {
// This should never happen
self.Error(gst.DomainResource, gst.ResourceErrorFailed,
"Could not retrieve fileinfo on opened file",
err.Error(),
)
return false, 0
}
return true, stat.Size()
}
// Start is called to start this element. In this example, the configured file is opened for reading,
// and any error encountered in the process is posted to the pipeline.
func (f *fileSrc) Start(self *base.GstBaseSrc) bool {
if f.state.started {
self.Error(gst.DomainResource, gst.ResourceErrorSettings, "FileSrc is already started", "")
return false
}
if f.settings.location == "" {
self.Error(gst.DomainResource, gst.ResourceErrorSettings, "File location is not defined", "")
return false
}
var err error
f.state.file, err = os.OpenFile(f.settings.location, syscall.O_RDONLY, 0444)
if err != nil {
self.Error(gst.DomainResource, gst.ResourceErrorOpenRead,
fmt.Sprintf("Could not open file %s for reading", f.settings.location), err.Error())
return false
}
f.state.position = 0
f.state.started = true
self.StartComplete(gst.FlowOK)
self.Info(gst.DomainResource, "Started")
return true
}
// Stop is called to stop the element. The file is closed and the local values are zeroed out.
func (f *fileSrc) Stop(self *base.GstBaseSrc) bool {
if !f.state.started {
self.Error(gst.DomainResource, gst.ResourceErrorSettings, "FileSrc is not started", "")
return false
}
if err := f.state.file.Close(); err != nil {
self.Error(gst.DomainResource, gst.ResourceErrorClose, "Failed to close the source file", err.Error())
return false
}
f.state.file = nil
f.state.position = 0
f.state.started = false
self.Info(gst.DomainResource, "Stopped")
return true
}
// Fill is called to fill a pre-allocated buffer with the data at offset to the given size.
// Since we declared that we are seekable, we need to support the provided offset not neccesarily matching
// where we currently are in the file. This is why we store the position in the file locally.
func (f *fileSrc) Fill(self *base.GstBaseSrc, offset uint64, size uint, buffer *gst.Buffer) gst.FlowReturn {
if !f.state.started || f.state.file == nil {
self.Error(gst.DomainCore, gst.CoreErrorFailed, "Not started yet", "")
return gst.FlowError
}
if f.state.position != offset {
if _, err := f.state.file.Seek(int64(offset), 0); err != nil {
self.Error(gst.DomainResource, gst.ResourceErrorSeek,
fmt.Sprintf("Failed to seek to %d in file", offset), err.Error())
return gst.FlowError
}
}
out := make([]byte, int(size))
if _, err := f.state.file.Read(out); err != nil && err != io.EOF {
self.Error(gst.DomainResource, gst.ResourceErrorRead,
fmt.Sprintf("Failed to read %d bytes from file at %d", size, offset), err.Error())
return gst.FlowError
}
f.state.position = f.state.position + uint64(size)
bufmap := buffer.Map(gst.MapWrite)
if bufmap == nil {
self.Error(gst.DomainLibrary, gst.LibraryErrorFailed, "Failed to map buffer", "")
return gst.FlowError
}
defer buffer.Unmap()
bufmap.WriteData(out)
buffer.SetSize(int64(size))
return gst.FlowOK
}
// URIHandler implementations are the methods required by the GstURIHandler interface.
// GetURI returns the currently configured URI
func (f *fileSrc) GetURI() string { return fmt.Sprintf("file://%s", f.settings.location) }
// GetURIType returns the types of URI this element supports.
func (f *fileSrc) GetURIType() gst.URIType { return gst.URISource }
// GetProtocols returns the protcols this element supports.
func (f *fileSrc) GetProtocols() []string { return []string{"file"} }
// SetURI should set the URI that this element is working on.
func (f *fileSrc) SetURI(uri string) (bool, error) {
if uri == "file://" {
return true, nil
}
err := f.setLocation(uri)
if err != nil {
return false, err
}
return true, nil
}

View File

@@ -0,0 +1,46 @@
// The contents of this file could be generated from markers placed in filesrc.go
package main
import "C"
import (
"unsafe"
"github.com/tinyzimmer/go-gst/gst"
"github.com/tinyzimmer/go-gst/gst/base"
)
// The metadata for this plugin
var pluginMeta = &gst.PluginMetadata{
MajorVersion: gst.VersionMajor,
MinorVersion: gst.VersionMinor,
Name: "go-file-plugins",
Description: "File plugins written in Go",
Version: "v0.0.1",
License: gst.LicenseLGPL,
Source: "go-gst",
Package: "examples",
Origin: "https://github.com/tinyzimmer/go-gst",
ReleaseDate: "2021-01-04",
// The init function is called to register elements provided
// by the plugin.
Init: func(plugin *gst.Plugin) bool {
return gst.RegisterElement(
plugin,
"gofilesrc", // The name of the element
gst.RankNone, // The rank of the element
&fileSrc{}, // The GoElement implementation for the element
base.ExtendsBaseSrc, // The base subclass this element extends
)
},
}
// A single method must be exported from the compiled library that provides for GStreamer
// to fetch the description and init function for this plugin. The name of the method
// must match the format gst_plugin_NAME_get_desc, where hyphens are replaced with underscores.
//export gst_plugin_gofilesrc_get_desc
func gst_plugin_gofilesrc_get_desc() unsafe.Pointer { return pluginMeta.Export() }
// main is left unimplemented since these files are compiled to c-shared.
func main() {}

1
go.sum
View File

@@ -1,4 +1,5 @@
github.com/gotk3/gotk3 v0.4.0 h1:TIuhyQitGeRTxOQIV3AJlYtEWWJpC74JHwAIsxlH8MU=
github.com/gotk3/gotk3 v0.4.0/go.mod h1:Eew3QBwAOBTrfFFDmsDE5wZWbcagBL1NUslj1GhRveo=
github.com/gotk3/gotk3 v0.5.2 h1:jbSFvUNMfo3ImM6BWBAkNUxY5piqP3eTc1YFbYy9ecU=
github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0=
github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc=

View File

@@ -1,5 +1,10 @@
#ifndef __GST_APP_GO_H__
#define __GST_APP_GO_H__
#include <gst/app/gstappsink.h>
#include <gst/app/gstappsrc.h>
inline GstAppSink * toGstAppSink (void *p) { return (GST_APP_SINK(p)); }
inline GstAppSrc * toGstAppSrc (void *p) { return (GST_APP_SRC(p)); }
#endif

2
gst/base/doc.go Normal file
View File

@@ -0,0 +1,2 @@
// Package base contains bindings for extendable GStreamer base objects.
package base

9
gst/base/gst.go.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef __GST_BASE_GO_H__
#define __GST_BASE_GO_H__
#include <gst/base/gstbasesrc.h>
inline GstBaseSrc * toGstBaseSrc (void *p) { return GST_BASE_SRC_CAST(p); };
inline GstBaseSrcClass * toGstBaseSrcClass (void *p) { return (GstBaseSrcClass *)p; };
#endif

47
gst/base/gst_base_src.go Normal file
View File

@@ -0,0 +1,47 @@
package base
/*
#include "gst.go.h"
*/
import "C"
import (
"unsafe"
"github.com/tinyzimmer/go-gst/gst"
)
// GstBaseSrc represents a GstBaseSrc.
type GstBaseSrc struct{ *gst.Element }
// ToGstBaseSrc returns a GstBaseSrc object for the given object.
func ToGstBaseSrc(obj *gst.Object) *GstBaseSrc {
return &GstBaseSrc{&gst.Element{Object: obj}}
}
// wrapGstBaseSrc wraps the given unsafe.Pointer in a GstBaseSrc instance.
func wrapGstBaseSrc(obj *C.GstBaseSrc) *GstBaseSrc {
return &GstBaseSrc{gst.FromGstElementUnsafe(unsafe.Pointer(obj))}
}
// Instance returns the underlying C GstBaseSrc instance
func (g *GstBaseSrc) Instance() *C.GstBaseSrc {
return C.toGstBaseSrc(g.Unsafe())
}
// SetFormat sets the default format of the source. This will be the format used for sending
// SEGMENT events and for performing seeks.
//
// If a format of gst.FormatBytes is set, the element will be able to operate in pull mode if the
// IsSeekable returns TRUE.
//
// This function must only be called in when the element is paused.
func (g *GstBaseSrc) SetFormat(format gst.Format) {
C.gst_base_src_set_format(g.Instance(), C.GstFormat(format))
}
// StartComplete completes an asynchronous start operation. When the subclass overrides the start method,
// it should call StartComplete when the start operation completes either from the same thread or from an
// asynchronous helper thread.
func (g *GstBaseSrc) StartComplete(ret gst.FlowReturn) {
C.gst_base_src_start_complete(g.Instance(), C.GstFlowReturn(ret))
}

View File

@@ -0,0 +1,198 @@
package base
//#include "gst.go.h"
import "C"
import (
"time"
"unsafe"
"github.com/tinyzimmer/go-gst/gst"
)
//export goGstBaseSrcGetCaps
func goGstBaseSrcGetCaps(src *C.GstBaseSrc, filter *C.GstCaps) *C.GstCaps {
elem := gst.FromObjectUnsafePrivate(unsafe.Pointer(src))
caller := elem.(interface {
GetCaps(*GstBaseSrc, *gst.Caps) *gst.Caps
})
res := caller.GetCaps(wrapGstBaseSrc(src), gst.FromGstCapsUnsafe(unsafe.Pointer(filter)))
return (*C.GstCaps)(unsafe.Pointer(res.Instance()))
}
//export goGstBaseSrcNegotiate
func goGstBaseSrcNegotiate(src *C.GstBaseSrc) C.gboolean {
elem := gst.FromObjectUnsafePrivate(unsafe.Pointer(src))
caller := elem.(interface {
Negotiate(*GstBaseSrc) bool
})
return gboolean(caller.Negotiate(wrapGstBaseSrc(src)))
}
//export goGstBaseSrcFixate
func goGstBaseSrcFixate(src *C.GstBaseSrc, caps *C.GstCaps) *C.GstCaps {
elem := gst.FromObjectUnsafePrivate(unsafe.Pointer(src))
caller := elem.(interface {
Fixate(*GstBaseSrc, *gst.Caps) *gst.Caps
})
res := caller.Fixate(wrapGstBaseSrc(src), gst.FromGstCapsUnsafe(unsafe.Pointer(caps)))
return (*C.GstCaps)(unsafe.Pointer(res.Instance()))
}
//export goGstBaseSrcSetCaps
func goGstBaseSrcSetCaps(src *C.GstBaseSrc, filter *C.GstCaps) C.gboolean {
elem := gst.FromObjectUnsafePrivate(unsafe.Pointer(src))
caller := elem.(interface {
SetCaps(*GstBaseSrc, *gst.Caps) bool
})
return gboolean(caller.SetCaps(wrapGstBaseSrc(src), gst.FromGstCapsUnsafe(unsafe.Pointer(filter))))
}
//export goGstBaseSrcDecideAllocation
func goGstBaseSrcDecideAllocation(src *C.GstBaseSrc, query *C.GstQuery) C.gboolean {
elem := gst.FromObjectUnsafePrivate(unsafe.Pointer(src))
caller := elem.(interface {
DecideAllocation(*GstBaseSrc, *gst.Query) bool
})
return gboolean(caller.DecideAllocation(wrapGstBaseSrc(src), gst.FromGstQueryUnsafe(unsafe.Pointer(query))))
}
//export goGstBaseSrcStart
func goGstBaseSrcStart(src *C.GstBaseSrc) C.gboolean {
elem := gst.FromObjectUnsafePrivate(unsafe.Pointer(src))
caller := elem.(interface {
Start(*GstBaseSrc) bool
})
return gboolean(caller.Start(wrapGstBaseSrc(src)))
}
//export goGstBaseSrcStop
func goGstBaseSrcStop(src *C.GstBaseSrc) C.gboolean {
elem := gst.FromObjectUnsafePrivate(unsafe.Pointer(src))
caller := elem.(interface {
Stop(*GstBaseSrc) bool
})
return gboolean(caller.Stop(wrapGstBaseSrc(src)))
}
//export goGstBaseSrcGetTimes
func goGstBaseSrcGetTimes(src *C.GstBaseSrc, buf *C.GstBuffer, start *C.GstClockTime, end *C.GstClockTime) {
elem := gst.FromObjectUnsafePrivate(unsafe.Pointer(src))
caller := elem.(interface {
GetTimes(*GstBaseSrc, *gst.Buffer) (start, end time.Duration)
})
gostart, goend := caller.GetTimes(wrapGstBaseSrc(src), gst.FromGstBufferUnsafe(unsafe.Pointer(buf)))
*start = C.GstClockTime(gostart.Nanoseconds())
*end = C.GstClockTime(goend.Nanoseconds())
}
//export goGstBaseSrcGetSize
func goGstBaseSrcGetSize(src *C.GstBaseSrc, size *C.guint64) C.gboolean {
elem := gst.FromObjectUnsafePrivate(unsafe.Pointer(src))
caller := elem.(interface {
GetSize(*GstBaseSrc) (bool, int64)
})
ok, gosize := caller.GetSize(wrapGstBaseSrc(src))
if !ok {
return gboolean(ok)
}
*size = C.guint64(gosize)
return gboolean(ok)
}
//export goGstBaseSrcIsSeekable
func goGstBaseSrcIsSeekable(src *C.GstBaseSrc) C.gboolean {
elem := gst.FromObjectUnsafePrivate(unsafe.Pointer(src))
caller := elem.(interface {
IsSeekable(*GstBaseSrc) bool
})
return gboolean(caller.IsSeekable(wrapGstBaseSrc(src)))
}
//export goGstBaseSrcPrepareSeekSegment
func goGstBaseSrcPrepareSeekSegment(src *C.GstBaseSrc, seek *C.GstEvent, segment *C.GstSegment) C.gboolean {
elem := gst.FromObjectUnsafePrivate(unsafe.Pointer(src))
caller := elem.(interface {
PrepareSeekSegment(*GstBaseSrc, *gst.Event, *gst.Segment) bool
})
return gboolean(caller.PrepareSeekSegment(wrapGstBaseSrc(src), gst.FromGstEventUnsafe(unsafe.Pointer(seek)), gst.FromGstSegmentUnsafe(unsafe.Pointer(segment))))
}
//export goGstBaseSrcDoSeek
func goGstBaseSrcDoSeek(src *C.GstBaseSrc, segment *C.GstSegment) C.gboolean {
elem := gst.FromObjectUnsafePrivate(unsafe.Pointer(src))
caller := elem.(interface {
DoSeek(*GstBaseSrc, *gst.Segment) bool
})
return gboolean(caller.DoSeek(wrapGstBaseSrc(src), gst.FromGstSegmentUnsafe(unsafe.Pointer(segment))))
}
//export goGstBaseSrcUnlock
func goGstBaseSrcUnlock(src *C.GstBaseSrc) C.gboolean {
elem := gst.FromObjectUnsafePrivate(unsafe.Pointer(src))
caller := elem.(interface {
Unlock(*GstBaseSrc) bool
})
return gboolean(caller.Unlock(wrapGstBaseSrc(src)))
}
//export goGstBaseSrcUnlockStop
func goGstBaseSrcUnlockStop(src *C.GstBaseSrc) C.gboolean {
elem := gst.FromObjectUnsafePrivate(unsafe.Pointer(src))
caller := elem.(interface {
UnlockStop(*GstBaseSrc) bool
})
return gboolean(caller.UnlockStop(wrapGstBaseSrc(src)))
}
//export goGstBaseSrcQuery
func goGstBaseSrcQuery(src *C.GstBaseSrc, query *C.GstQuery) C.gboolean {
elem := gst.FromObjectUnsafePrivate(unsafe.Pointer(src))
caller := elem.(interface {
Query(*GstBaseSrc, *gst.Query) bool
})
return gboolean(caller.Query(wrapGstBaseSrc(src), gst.FromGstQueryUnsafe(unsafe.Pointer(query))))
}
//export goGstBaseSrcEvent
func goGstBaseSrcEvent(src *C.GstBaseSrc, event *C.GstEvent) C.gboolean {
elem := gst.FromObjectUnsafePrivate(unsafe.Pointer(src))
caller := elem.(interface {
Event(*GstBaseSrc, *gst.Event) bool
})
return gboolean(caller.Event(wrapGstBaseSrc(src), gst.FromGstEventUnsafe(unsafe.Pointer(event))))
}
//export goGstBaseSrcCreate
func goGstBaseSrcCreate(src *C.GstBaseSrc, offset C.guint64, size C.guint, buf **C.GstBuffer) C.GstFlowReturn {
elem := gst.FromObjectUnsafePrivate(unsafe.Pointer(src))
caller := elem.(interface {
Create(*GstBaseSrc, uint64, uint) (gst.FlowReturn, *gst.Buffer)
})
ret, gobuf := caller.Create(wrapGstBaseSrc(src), uint64(offset), uint(size))
if ret == gst.FlowOK {
C.memcpy(unsafe.Pointer(*buf), unsafe.Pointer(gobuf.Instance()), C.sizeof_GstBuffer)
}
return C.GstFlowReturn(ret)
}
//export goGstBaseSrcAlloc
func goGstBaseSrcAlloc(src *C.GstBaseSrc, offset C.guint64, size C.guint, buf **C.GstBuffer) C.GstFlowReturn {
elem := gst.FromObjectUnsafePrivate(unsafe.Pointer(src))
caller := elem.(interface {
Alloc(*GstBaseSrc, uint64, uint) (gst.FlowReturn, *gst.Buffer)
})
ret, gobuf := caller.Alloc(wrapGstBaseSrc(src), uint64(offset), uint(size))
if ret == gst.FlowOK {
C.memcpy(unsafe.Pointer(*buf), unsafe.Pointer(gobuf.Instance()), C.sizeof_GstBuffer)
}
return C.GstFlowReturn(ret)
}
//export goGstBaseSrcFill
func goGstBaseSrcFill(src *C.GstBaseSrc, offset C.guint64, size C.guint, buf *C.GstBuffer) C.GstFlowReturn {
elem := gst.FromObjectUnsafePrivate(unsafe.Pointer(src))
caller := elem.(interface {
Fill(*GstBaseSrc, uint64, uint, *gst.Buffer) gst.FlowReturn
})
return C.GstFlowReturn(caller.Fill(wrapGstBaseSrc(src), uint64(offset), uint(size), gst.FromGstBufferUnsafe(unsafe.Pointer(buf))))
}

View File

@@ -0,0 +1,236 @@
package base
/*
#include "gst.go.h"
extern GstCaps * goGstBaseSrcGetCaps (GstBaseSrc * src, GstCaps * caps);
extern gboolean goGstBaseSrcNegotiate (GstBaseSrc * src);
extern GstCaps * goGstBaseSrcFixate (GstBaseSrc * src, GstCaps * caps);
extern gboolean goGstBaseSrcSetCaps (GstBaseSrc * src, GstCaps * filter);
extern gboolean goGstBaseSrcDecideAllocation (GstBaseSrc * src, GstQuery * query);
extern gboolean goGstBaseSrcStart (GstBaseSrc * src);
extern gboolean goGstBaseSrcStop (GstBaseSrc * src);
extern void goGstBaseSrcGetTimes (GstBaseSrc * src, GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
extern gboolean goGstBaseSrcGetSize (GstBaseSrc * src, guint64 * size);
extern gboolean goGstBaseSrcIsSeekable (GstBaseSrc * src);
extern gboolean goGstBaseSrcPrepareSeekSegment (GstBaseSrc * src, GstEvent * seek, GstSegment * segment);
extern gboolean goGstBaseSrcDoSeek (GstBaseSrc * src, GstSegment * segment);
extern gboolean goGstBaseSrcUnlock (GstBaseSrc * src);
extern gboolean goGstBaseSrcUnlockStop (GstBaseSrc * src);
extern gboolean goGstBaseSrcQuery (GstBaseSrc * src, GstQuery * query);
extern gboolean goGstBaseSrcEvent (GstBaseSrc * src, GstEvent * event);
extern GstFlowReturn goGstBaseSrcCreate (GstBaseSrc * src, guint64 offset, guint size, GstBuffer ** buffer);
extern GstFlowReturn goGstBaseSrcAlloc (GstBaseSrc * src, guint64 offset, guint size, GstBuffer ** buffer);
extern GstFlowReturn goGstBaseSrcFill (GstBaseSrc * src, guint64 offset, guint size, GstBuffer * buffer);
void setGstBaseSrcGetCaps (GstBaseSrcClass * klass) { klass->get_caps = goGstBaseSrcGetCaps; }
void setGstBaseSrcNegotiate (GstBaseSrcClass * klass) { klass->negotiate = goGstBaseSrcNegotiate; }
void setGstBaseSrcFixate (GstBaseSrcClass * klass) { klass->fixate = goGstBaseSrcFixate; }
void setGstBaseSrcSetCaps (GstBaseSrcClass * klass) { klass->set_caps = goGstBaseSrcSetCaps; }
void setGstBaseSrcDecideAllocation (GstBaseSrcClass * klass) { klass->decide_allocation = goGstBaseSrcDecideAllocation; }
void setGstBaseSrcStart (GstBaseSrcClass * klass) { klass->start = goGstBaseSrcStart; }
void setGstBaseSrcStop (GstBaseSrcClass * klass) { klass->stop = goGstBaseSrcStop; }
void setGstBaseSrcGetTimes (GstBaseSrcClass * klass) { klass->get_times = goGstBaseSrcGetTimes; }
void setGstBaseSrcGetSize (GstBaseSrcClass * klass) { klass->get_size = goGstBaseSrcGetSize; }
void setGstBaseSrcIsSeekable (GstBaseSrcClass * klass) { klass->is_seekable = goGstBaseSrcIsSeekable; }
void setGstBaseSrcPrepareSeekSegment (GstBaseSrcClass * klass) { klass->prepare_seek_segment = goGstBaseSrcPrepareSeekSegment; }
void setGstBaseSrcDoSeek (GstBaseSrcClass * klass) { klass->do_seek = goGstBaseSrcDoSeek; }
void setGstBaseSrcUnlock (GstBaseSrcClass * klass) { klass->unlock = goGstBaseSrcUnlock; }
void setGstBaseSrcUnlockStop (GstBaseSrcClass * klass) { klass->unlock_stop = goGstBaseSrcUnlockStop; }
void setGstBaseSrcQuery (GstBaseSrcClass * klass) { klass->query = goGstBaseSrcQuery; }
void setGstBaseSrcEvent (GstBaseSrcClass * klass) { klass->event = goGstBaseSrcEvent; }
void setGstBaseSrcCreate (GstBaseSrcClass * klass) { klass->create = goGstBaseSrcCreate; }
void setGstBaseSrcAlloc (GstBaseSrcClass * klass) { klass->alloc = goGstBaseSrcAlloc; }
void setGstBaseSrcFill (GstBaseSrcClass * klass) { klass->fill = goGstBaseSrcFill; }
*/
import "C"
import (
"time"
"unsafe"
"github.com/gotk3/gotk3/glib"
"github.com/tinyzimmer/go-gst/gst"
)
var (
// ExtendsBaseSrc is an Extendable for extending a GstBaseSrc
ExtendsBaseSrc gst.Extendable = &extendsBaseSrc{}
)
// GstBaseSrcImpl is the documented interface for an element extending a GstBaseSrc. It does not have to
// be implemented in it's entirety. Each of the methods it declares will be checked for their presence
// in the initializing object, and if the object declares an override it will replace the default
// implementation in the virtual methods.
type GstBaseSrcImpl interface {
// GetCaps retrieves the caps for this class.
GetCaps(*GstBaseSrc, *gst.Caps) *gst.Caps
// Negotiate decides on the caps for this source.
Negotiate(*GstBaseSrc) bool
// Fixate is called if, during negotiation, caps need fixating.
Fixate(*GstBaseSrc, *gst.Caps) *gst.Caps
// SetCaps is used to notify this class of new caps.
SetCaps(*GstBaseSrc, *gst.Caps) bool
// DecideAllocation sets up an allocation query.
DecideAllocation(*GstBaseSrc, *gst.Query) bool
// Start the source, ideal for opening resources.
Start(*GstBaseSrc) bool
// Stop the source, ideal for closing resources.
Stop(*GstBaseSrc) bool
// GetTimes should, given a buffer, return start and stop time when it should be pushed.
// The base class will sync on the clock using these times.
GetTimes(*GstBaseSrc, *gst.Buffer) (start, end time.Duration)
// GetSize should get the total size of the resource in bytes.
GetSize(*GstBaseSrc) (bool, int64)
// IsSeekable should check if the resource is seekable.
IsSeekable(*GstBaseSrc) bool
// PrepareSeekSegment prepares the segment on which to perform DoSeek, converting to the
// current basesrc format.
PrepareSeekSegment(*GstBaseSrc, *gst.Event, *gst.Segment) bool
// DoSeek is used to notify subclasses of a seek.
DoSeek(*GstBaseSrc, *gst.Segment) bool
// Unlock should unlock any pending access to the resource. Subclasses should perform the unlock
// ASAP.
Unlock(*GstBaseSrc) bool
// UnlockStop should clear any pending unlock request, as we succeeded in unlocking.
UnlockStop(*GstBaseSrc) bool
// Query is used to notify subclasses of a query.
Query(*GstBaseSrc, *gst.Query) bool
// Event is used to notify subclasses of an event.
Event(*GstBaseSrc, *gst.Event) bool
// Create asks the subclass to create a buffer with offset and size. The default implementation
// will call alloc and fill.
Create(self *GstBaseSrc, offset uint64, size uint) (gst.FlowReturn, *gst.Buffer)
// Alloc asks the subclass to allocate an output buffer. The default implementation will use the negotiated
// allocator.
Alloc(self *GstBaseSrc, offset uint64, size uint) (gst.FlowReturn, *gst.Buffer)
// Fill asks the subclass to fill the buffer with data from offset and size.
Fill(self *GstBaseSrc, offset uint64, size uint, buffer *gst.Buffer) gst.FlowReturn
}
type extendsBaseSrc struct{}
func (e *extendsBaseSrc) Type() glib.Type { return glib.Type(C.gst_base_src_get_type()) }
func (e *extendsBaseSrc) ClassSize() int64 { return int64(C.sizeof_GstBaseSrcClass) }
func (e *extendsBaseSrc) InstanceSize() int64 { return int64(C.sizeof_GstBaseSrc) }
// InitClass iterates the methods provided by the element and overrides any provided
// in the virtual methods.
func (e *extendsBaseSrc) InitClass(klass unsafe.Pointer, elem gst.GoElement) {
class := C.toGstBaseSrcClass(klass)
if _, ok := elem.(interface {
GetCaps(*GstBaseSrc, *gst.Caps) *gst.Caps
}); ok {
C.setGstBaseSrcGetCaps(class)
}
if _, ok := elem.(interface {
Negotiate(*GstBaseSrc) bool
}); ok {
C.setGstBaseSrcNegotiate(class)
}
if _, ok := elem.(interface {
Fixate(*GstBaseSrc, *gst.Caps) *gst.Caps
}); ok {
C.setGstBaseSrcFixate(class)
}
if _, ok := elem.(interface {
SetCaps(*GstBaseSrc, *gst.Caps) bool
}); ok {
C.setGstBaseSrcSetCaps(class)
}
if _, ok := elem.(interface {
DecideAllocation(*GstBaseSrc, *gst.Query) bool
}); ok {
C.setGstBaseSrcDecideAllocation(class)
}
if _, ok := elem.(interface {
Start(*GstBaseSrc) bool
}); ok {
C.setGstBaseSrcStart(class)
}
if _, ok := elem.(interface {
Stop(*GstBaseSrc) bool
}); ok {
C.setGstBaseSrcStop(class)
}
if _, ok := elem.(interface {
GetTimes(*GstBaseSrc, *gst.Buffer) (start, end time.Duration)
}); ok {
C.setGstBaseSrcGetTimes(class)
}
if _, ok := elem.(interface {
GetSize(*GstBaseSrc) (bool, int64)
}); ok {
C.setGstBaseSrcGetSize(class)
}
if _, ok := elem.(interface {
IsSeekable(*GstBaseSrc) bool
}); ok {
C.setGstBaseSrcIsSeekable(class)
}
if _, ok := elem.(interface {
PrepareSeekSegment(*GstBaseSrc, *gst.Event, *gst.Segment) bool
}); ok {
C.setGstBaseSrcPrepareSeekSegment(class)
}
if _, ok := elem.(interface {
DoSeek(*GstBaseSrc, *gst.Segment) bool
}); ok {
C.setGstBaseSrcDoSeek(class)
}
if _, ok := elem.(interface {
Unlock(*GstBaseSrc) bool
}); ok {
C.setGstBaseSrcUnlock(class)
}
if _, ok := elem.(interface {
UnlockStop(*GstBaseSrc) bool
}); ok {
C.setGstBaseSrcUnlockStop(class)
}
if _, ok := elem.(interface {
Query(*GstBaseSrc, *gst.Query) bool
}); ok {
C.setGstBaseSrcQuery(class)
}
if _, ok := elem.(interface {
Event(*GstBaseSrc, *gst.Event) bool
}); ok {
C.setGstBaseSrcEvent(class)
}
if _, ok := elem.(interface {
Create(self *GstBaseSrc, offset uint64, size uint) (gst.FlowReturn, *gst.Buffer)
}); ok {
C.setGstBaseSrcCreate(class)
}
if _, ok := elem.(interface {
Alloc(self *GstBaseSrc, offset uint64, size uint) (gst.FlowReturn, *gst.Buffer)
}); ok {
C.setGstBaseSrcAlloc(class)
}
if _, ok := elem.(interface {
Fill(self *GstBaseSrc, offset uint64, size uint, buffer *gst.Buffer) gst.FlowReturn
}); ok {
C.setGstBaseSrcFill(class)
}
}

7
gst/base/pkg_config.go Normal file
View File

@@ -0,0 +1,7 @@
package base
/*
#cgo pkg-config: gstreamer-1.0 gstreamer-base-1.0
#cgo CFLAGS: -Wno-deprecated-declarations -g -Wall
*/
import "C"

12
gst/base/util.go Normal file
View File

@@ -0,0 +1,12 @@
package base
//#include "gst.go.h"
import "C"
// gboolean converts a go bool to a C.gboolean.
func gboolean(b bool) C.gboolean {
if b {
return C.gboolean(1)
}
return C.gboolean(0)
}

View File

@@ -111,3 +111,13 @@ func glistToStreamSlice(glist *C.GList) []*Stream {
})
return out
}
func glistToPadTemplateSlice(glist *C.GList) []*PadTemplate {
l := glib.WrapList(uintptr(unsafe.Pointer(&glist)))
out := make([]*PadTemplate, 0)
l.FreeFull(func(item interface{}) {
tmpl := item.(*C.GstPadTemplate)
out = append(out, wrapPadTemplate(toGObject(unsafe.Pointer(tmpl))))
})
return out
}

View File

@@ -3,10 +3,13 @@ package gst
// CGO exports have to be defined in a separate file from where they are used or else
// there will be double linkage issues.
// #include <gst/gst.h>
/*
#include <gst/gst.h>
*/
import "C"
import (
"reflect"
"unsafe"
"github.com/gotk3/gotk3/glib"
@@ -16,6 +19,7 @@ import (
//export goElementCallAsync
func goElementCallAsync(element *C.GstElement, userData C.gpointer) {
iface := gopointer.Restore(unsafe.Pointer(userData))
defer gopointer.Unref(unsafe.Pointer(userData))
f := iface.(func())
f()
}
@@ -221,3 +225,115 @@ func goClockCb(gclock *C.GstClock, clockTime C.GstClockTime, clockID C.GstClockI
clock := wrapClock(&glib.Object{GObject: glib.ToGObject(unsafe.Pointer(gclock))})
return gboolean(cb(clock, ClockTime(clockTime)))
}
//export goPluginInit
func goPluginInit(plugin *C.GstPlugin, userData C.gpointer) C.gboolean {
ptr := unsafe.Pointer(userData)
defer gopointer.Unref(ptr)
funcIface := gopointer.Restore(ptr)
cb, ok := funcIface.(PluginInitFunc)
if !ok {
return gboolean(false)
}
return gboolean(cb(wrapPlugin(&glib.Object{GObject: glib.ToGObject(unsafe.Pointer(plugin))})))
}
//export goGlobalPluginInit
func goGlobalPluginInit(plugin *C.GstPlugin) C.gboolean {
return gboolean(globalPluginInit(wrapPlugin(&glib.Object{GObject: glib.ToGObject(unsafe.Pointer(plugin))})))
}
//export goClassInit
func goClassInit(klass C.gpointer, klassData C.gpointer) {
registerMutex.Lock()
defer registerMutex.Unlock()
ptr := unsafe.Pointer(klassData)
iface := gopointer.Restore(ptr)
defer gopointer.Unref(ptr)
data := iface.(*classData)
registeredClasses[klass] = data.elem
data.ext.InitClass(unsafe.Pointer(klass), data.elem)
C.g_type_class_add_private(klass, C.gsize(unsafe.Sizeof(uintptr(0))))
data.elem.ClassInit(wrapElementClass(klass))
}
//export goInstanceInit
func goInstanceInit(obj *C.GTypeInstance, klass C.gpointer) {
registerMutex.Lock()
defer registerMutex.Unlock()
elem := registeredClasses[klass].New()
registeredClasses[klass] = elem
ptr := gopointer.Save(elem)
private := C.g_type_instance_get_private(obj, registeredTypes[reflect.TypeOf(registeredClasses[klass]).String()])
C.memcpy(unsafe.Pointer(private), unsafe.Pointer(&ptr), C.gulong(unsafe.Sizeof(uintptr(0))))
}
//export goURIHdlrGetURIType
func goURIHdlrGetURIType(gtype C.GType) C.GstURIType {
return C.GstURIType(globalURIHdlr.GetURIType())
}
//export goURIHdlrGetProtocols
func goURIHdlrGetProtocols(gtype C.GType) **C.gchar {
protocols := globalURIHdlr.GetProtocols()
size := C.size_t(unsafe.Sizeof((*C.gchar)(nil)))
length := C.size_t(len(protocols))
arr := (**C.gchar)(C.malloc(length * size))
view := (*[1 << 30]*C.gchar)(unsafe.Pointer(arr))[0:len(protocols):len(protocols)]
for i, proto := range protocols {
view[i] = (*C.gchar)(C.CString(proto))
}
return arr
}
//export goURIHdlrGetURI
func goURIHdlrGetURI(hdlr *C.GstURIHandler) *C.gchar {
iface := FromObjectUnsafePrivate(unsafe.Pointer(hdlr))
return (*C.gchar)(unsafe.Pointer(C.CString(iface.(URIHandler).GetURI())))
}
//export goURIHdlrSetURI
func goURIHdlrSetURI(hdlr *C.GstURIHandler, uri *C.gchar, gerr **C.GError) C.gboolean {
iface := FromObjectUnsafePrivate(unsafe.Pointer(hdlr))
ok, err := iface.(URIHandler).SetURI(C.GoString(uri))
if err != nil {
C.g_set_error_literal(gerr, newQuarkFromString(string(DomainLibrary)), C.gint(LibraryErrorSettings), C.CString(err.Error()))
}
return gboolean(ok)
}
//export goObjectSetProperty
func goObjectSetProperty(obj *C.GObject, propID C.guint, val *C.GValue, param *C.GParamSpec) {
iface := FromObjectUnsafePrivate(unsafe.Pointer(obj))
iface.SetProperty(wrapObject(toGObject(unsafe.Pointer(obj))), uint(propID-1), glib.ValueFromNative(unsafe.Pointer(val)))
}
//export goObjectGetProperty
func goObjectGetProperty(obj *C.GObject, propID C.guint, value *C.GValue, param *C.GParamSpec) {
iface := FromObjectUnsafePrivate(unsafe.Pointer(obj))
val := iface.GetProperty(wrapObject(toGObject(unsafe.Pointer(obj))), uint(propID-1))
if val == nil {
return
}
C.g_value_copy((*C.GValue)(unsafe.Pointer(val.GValue)), value)
}
//export goObjectConstructed
func goObjectConstructed(obj *C.GObject) {
iface := FromObjectUnsafePrivate(unsafe.Pointer(obj))
iface.Constructed(wrapObject(toGObject(unsafe.Pointer(obj))))
}
//export goObjectFinalize
func goObjectFinalize(obj *C.GObject, klass C.gpointer) {
registerMutex.Lock()
defer registerMutex.Unlock()
delete(registeredClasses, klass)
gopointer.Unref(privateFromObj(unsafe.Pointer(obj)))
}

View File

@@ -5,6 +5,32 @@ import "C"
import "unsafe"
// Version represents information about the current GST version.
type Version int
const (
// VersionMajor is the major version number of the GStreamer core.
VersionMajor Version = C.GST_VERSION_MAJOR
// VersionMinor is the minor version number of the GStreamer core.
VersionMinor Version = C.GST_VERSION_MINOR
)
// License represents a type of license used on a plugin.
type License string
// Types of licenses
const (
LicenseLGPL License = "LGPL"
LicenseGPL License = "GPL"
LicenseQPL License = "QPL"
LicenseGPLQPL License = "GPL/QPL"
LicenseMPL License = "MPL"
LicenseBSD License = "BSD"
LicenseMIT License = "MIT/X11"
LicenseProprietary License = "Proprietary"
LicenseUnknown License = "unknown"
)
// GFraction is a helper structure for building fractions for functions that require them.
type GFraction struct {
num, denom int
@@ -382,20 +408,20 @@ type PadDirection int
// Type casting of pad directions
const (
PadUnknown PadDirection = C.GST_PAD_UNKNOWN // (0) - the direction is unknown
PadSource PadDirection = C.GST_PAD_SRC // (1) - the pad is a source pad
PadSink PadDirection = C.GST_PAD_SINK // (2) - the pad is a sink pad
PadDirectionUnknown PadDirection = C.GST_PAD_UNKNOWN // (0) - the direction is unknown
PadDirectionSource PadDirection = C.GST_PAD_SRC // (1) - the pad is a source pad
PadDirectionSink PadDirection = C.GST_PAD_SINK // (2) - the pad is a sink pad
)
// String implements a Stringer on PadDirection.
func (p PadDirection) String() string {
switch p {
case PadUnknown:
return "Unknown"
case PadSource:
return "Src"
case PadSink:
return "Sink"
case PadDirectionUnknown:
return "unknown"
case PadDirectionSource:
return "src"
case PadDirectionSink:
return "sink"
}
return ""
}
@@ -424,20 +450,20 @@ type PadPresence int
// Type casting of pad presences
const (
PadAlways PadPresence = C.GST_PAD_ALWAYS // (0) - the pad is always available
PadSometimes PadPresence = C.GST_PAD_SOMETIMES // (1) - the pad will become available depending on the media stream
PadRequest PadPresence = C.GST_PAD_REQUEST // (2) - the pad is only available on request with gst_element_request_pad.
PadPresenceAlways PadPresence = C.GST_PAD_ALWAYS // (0) - the pad is always available
PadPresenceSometimes PadPresence = C.GST_PAD_SOMETIMES // (1) - the pad will become available depending on the media stream
PadPresenceRequest PadPresence = C.GST_PAD_REQUEST // (2) - the pad is only available on request with gst_element_request_pad.
)
// String implements a stringer on PadPresence.
func (p PadPresence) String() string {
switch p {
case PadAlways:
return "Always"
case PadSometimes:
return "Sometimes"
case PadRequest:
return "Request"
case PadPresenceAlways:
return "always"
case PadPresenceSometimes:
return "sometimes"
case PadPresenceRequest:
return "request"
}
return ""
}

View File

@@ -7,7 +7,7 @@ type GError struct {
structure *Structure
// used for message constructors
code int
code ErrorCode
}
// Message is an alias to `Error()`. It's for clarity when this object
@@ -23,9 +23,11 @@ func (e *GError) DebugString() string { return e.debugStr }
// Structure returns the structure of the error message which may contain additional metadata.
func (e *GError) Structure() *Structure { return e.structure }
// NewGError wraps the given error inside a GError (to be used with message constructors). The code
// is optional and allows for adding additional "types" to the error.
func NewGError(code int, err error) *GError {
// Code returns the error code of the error message.
func (e *GError) Code() ErrorCode { return e.code }
// NewGError wraps the given error inside a GError (to be used with message constructors).
func NewGError(code ErrorCode, err error) *GError {
return &GError{
errMsg: err.Error(),
code: code,

75
gst/g_object_class.go Normal file
View File

@@ -0,0 +1,75 @@
package gst
/*
#include "gst.go.h"
extern GstURIType goURIHdlrGetURIType (GType type);
extern const gchar * const * goURIHdlrGetProtocols (GType type);
extern gchar * goURIHdlrGetURI (GstURIHandler * handler);
extern gboolean goURIHdlrSetURI (GstURIHandler * handler,
const gchar * uri,
GError ** error);
void uriHandlerInit (gpointer iface, gpointer iface_data)
{
((GstURIHandlerInterface*)iface)->get_type = goURIHdlrGetURIType;
((GstURIHandlerInterface*)iface)->get_protocols = goURIHdlrGetProtocols;
((GstURIHandlerInterface*)iface)->get_uri = goURIHdlrGetURI;
((GstURIHandlerInterface*)iface)->set_uri = goURIHdlrSetURI;
}
*/
import "C"
import (
"unsafe"
"github.com/gotk3/gotk3/glib"
)
// ObjectClass is a loose binding around the glib GObjectClass.
// It forms the base of a GstElementClass.
type ObjectClass struct {
ptr *C.GObjectClass
}
// Unsafe is a convenience wrapper to return the unsafe.Pointer of the underlying C instance.
func (o *ObjectClass) Unsafe() unsafe.Pointer { return unsafe.Pointer(o.ptr) }
// Instance returns the underlying C GObjectClass pointer
func (o *ObjectClass) Instance() *C.GObjectClass { return o.ptr }
// InstallProperties will install the given ParameterSpecs to the object class.
// They will be IDed in the order they are provided.
func (o *ObjectClass) InstallProperties(params []*ParameterSpec) {
for idx, prop := range params {
C.g_object_class_install_property(
o.Instance(),
C.guint(idx+1),
prop.paramSpec,
)
}
}
// TypeInstance is a loose binding around the glib GTypeInstance. It exposes methods required
// to register the various capabilities of an element.
type TypeInstance struct {
gtype C.GType
gotype GoElement
}
// AddInterface will add an interface implementation for the type referenced by this object.
func (t *TypeInstance) AddInterface(iface glib.Type) {
ifaceInfo := C.GInterfaceInfo{
interface_data: nil,
interface_finalize: nil,
}
switch iface {
case InterfaceURIHandler:
globalURIHdlr = t.gotype.(URIHandler)
ifaceInfo.interface_init = C.GInterfaceInitFunc(C.uriHandlerInit)
}
C.g_type_add_interface_static(
(C.GType)(t.gtype),
(C.GType)(iface),
&ifaceInfo,
)
}

View File

@@ -13,12 +13,61 @@ import (
// ParameterSpec is a go representation of a C GParamSpec
type ParameterSpec struct {
paramSpec *C.GParamSpec
Name string
Blurb string
Flags ParameterFlags
ValueType glib.Type
OwnerType glib.Type
DefaultValue *glib.Value
defaultValue *glib.Value
}
// NewStringParameter returns a new ParameterSpec that will hold a string value.
func NewStringParameter(name, nick, blurb string, defaultValue *string, flags ParameterFlags) *ParameterSpec {
var cdefault *C.gchar
var paramDefault *glib.Value
if defaultValue != nil {
cdefault = C.CString(*defaultValue)
var err error
paramDefault, err = glib.ValueInit(glib.TYPE_STRING)
if err != nil {
return nil
}
paramDefault.SetString(*defaultValue)
}
paramSpec := C.g_param_spec_string(
(*C.gchar)(C.CString(name)),
(*C.gchar)(C.CString(nick)),
(*C.gchar)(C.CString(blurb)),
(*C.gchar)(cdefault),
C.GParamFlags(flags),
)
return &ParameterSpec{paramSpec: paramSpec, defaultValue: paramDefault}
}
// Name returns the name of this parameter.
func (p *ParameterSpec) Name() string {
return C.GoString(C.g_param_spec_get_name(p.paramSpec))
}
// Blurb returns the blurb for this parameter.
func (p *ParameterSpec) Blurb() string {
return C.GoString(C.g_param_spec_get_blurb(p.paramSpec))
}
// Flags returns the flags for this parameter.
func (p *ParameterSpec) Flags() ParameterFlags {
return ParameterFlags(p.paramSpec.flags)
}
// ValueType returns the GType for the value inside this parameter.
func (p *ParameterSpec) ValueType() glib.Type {
return glib.Type(p.paramSpec.value_type)
}
// OwnerType returns the Gtype for the owner of this parameter.
func (p *ParameterSpec) OwnerType() glib.Type {
return glib.Type(p.paramSpec.owner_type)
}
// DefaultValue returns the default value for the parameter if it was included when the object
// was instantiated. Otherwise it returns nil.
func (p *ParameterSpec) DefaultValue() *glib.Value {
return p.defaultValue
}
// Unref the underlying paramater spec.
@@ -114,7 +163,10 @@ type FlagsValue struct {
// GetDefaultFlags returns the default flags for this parameter spec.
func (p *ParameterSpec) GetDefaultFlags() int {
return int(C.g_value_get_flags((*C.GValue)(p.DefaultValue.Native())))
if p.DefaultValue() == nil {
return 0
}
return int(C.g_value_get_flags((*C.GValue)(p.DefaultValue().Native())))
}
// GetFlagValues returns the possible flags for this parameter.
@@ -135,7 +187,10 @@ func (p *ParameterSpec) GetFlagValues() []*FlagsValue {
// GetCaps returns the caps in this parameter if it is of type GST_TYPE_CAPS.
func (p *ParameterSpec) GetCaps() *Caps {
caps := C.gst_value_get_caps((*C.GValue)(unsafe.Pointer(p.DefaultValue.Native())))
if p.DefaultValue() == nil {
return nil
}
caps := C.gst_value_get_caps((*C.GValue)(unsafe.Pointer(p.DefaultValue().Native())))
if caps == nil {
return nil
}
@@ -152,6 +207,7 @@ func (p ParameterFlags) Has(b ParameterFlags) bool { return p&b != 0 }
const (
ParameterReadable ParameterFlags = C.G_PARAM_READABLE // the parameter is readable
ParameterWritable = C.G_PARAM_WRITABLE // the parameter is writable
ParameterReadWrite = ParameterReadable | ParameterWritable
ParameterConstruct = C.G_PARAM_CONSTRUCT // the parameter will be set upon object construction
ParameterConstructOnly = C.G_PARAM_CONSTRUCT_ONLY // the parameter can only be set upon object construction
ParameterLaxValidation = C.G_PARAM_LAX_VALIDATION // upon parameter conversion (see g_param_value_convert()) strict validation is not required

View File

@@ -3,11 +3,15 @@
#include <stdlib.h>
#include <gst/gst.h>
#include <gst/base/gstbasesrc.h>
/*
Type Castings
*/
inline GType objectGType (GObject *obj) { return G_OBJECT_TYPE(obj); };
inline GObjectClass * toGObjectClass (void *p) { return (G_OBJECT_CLASS(p)); }
inline GstAllocator * toGstAllocator (void *p) { return (GST_ALLOCATOR_CAST(p)); }
inline GstBin * toGstBin (void *p) { return (GST_BIN(p)); }
inline GstBufferList * toGstBufferList (void *p) { return (GST_BUFFER_LIST(p)); }
@@ -20,6 +24,7 @@ inline GstClock * toGstClock (void *p) { return (GST_CLO
inline GstContext * toGstContext (void *p) { return (GST_CONTEXT_CAST(p)); }
inline GstDevice * toGstDevice (void *p) { return (GST_DEVICE_CAST(p)); }
inline GstElementFactory * toGstElementFactory (void *p) { return (GST_ELEMENT_FACTORY(p)); }
inline GstElementClass * toGstElementClass (void *p) { return (GST_ELEMENT_CLASS(p)); }
inline GstElement * toGstElement (void *p) { return (GST_ELEMENT(p)); }
inline GstEvent * toGstEvent (void *p) { return (GST_EVENT(p)); }
inline GstGhostPad * toGstGhostPad (void *p) { return (GST_GHOST_PAD(p)); }

View File

@@ -39,6 +39,12 @@ type Buffer struct {
mapInfo *MapInfo
}
// FromGstBufferUnsafe wraps the given C GstBuffer in the go type. It is meant for internal usage
// and exported for visibility to other packages.
func FromGstBufferUnsafe(buf unsafe.Pointer) *Buffer {
return wrapBuffer((*C.GstBuffer)(buf))
}
// NewEmptyBuffer returns a new empty buffer.
func NewEmptyBuffer() *Buffer {
return wrapBuffer(C.gst_buffer_new())

View File

@@ -21,6 +21,8 @@ import "C"
import (
"fmt"
"path"
"runtime"
"unsafe"
"github.com/gotk3/gotk3/glib"
@@ -30,6 +32,9 @@ import (
// Element is a Go wrapper around a GstElement.
type Element struct{ *Object }
// ToElement returns an Element object for the given Object.
func ToElement(obj *Object) *Element { return &Element{Object: obj} }
// ElementLinkMany is a go implementation of `gst_element_link_many` to compensate for
// no variadic functions in cgo.
func ElementLinkMany(elems ...*Element) error {
@@ -45,24 +50,145 @@ func ElementLinkMany(elems ...*Element) error {
return nil
}
// Rank represents a level of importance when autoplugging elements.
type Rank uint
// For now just a single RankNone is provided
const (
RankNone Rank = 0
)
// RegisterElement creates a new elementfactory capable of instantiating objects of the given GoElement
// and adds the factory to the plugin. A higher rank means more importance when autoplugging.
func RegisterElement(plugin *Plugin, name string, rank Rank, elem GoElement, extends Extendable) bool {
return gobool(C.gst_element_register(
plugin.Instance(),
C.CString(name),
C.guint(rank),
gtypeForGoElement(name, elem, extends),
))
}
// Instance returns the underlying GstElement instance.
func (e *Element) Instance() *C.GstElement { return C.toGstElement(e.Unsafe()) }
// Link wraps gst_element_link and links this element to the given one.
func (e *Element) Link(elem *Element) error {
if ok := C.gst_element_link((*C.GstElement)(e.Instance()), (*C.GstElement)(elem.Instance())); !gobool(ok) {
return fmt.Errorf("Failed to link %s to %s", e.Name(), elem.Name())
// AbortState aborts the state change of the element. This function is used by elements that do asynchronous state changes
// and find out something is wrong.
func (e *Element) AbortState() { C.gst_element_abort_state(e.Instance()) }
// AddPad adds a pad (link point) to element. pad's parent will be set to element
//
// Pads are automatically activated when added in the PAUSED or PLAYING state.
//
// The pad and the element should be unlocked when calling this function.
//
// This function will emit the pad-added signal on the element.
func (e *Element) AddPad(pad *Pad) bool {
return gobool(C.gst_element_add_pad(e.Instance(), pad.Instance()))
}
// BlockSetState is like SetState except it will block until the transition
// is complete.
func (e *Element) BlockSetState(state State) error {
if err := e.SetState(state); err != nil {
return err
}
cState := C.GstState(state)
var curState C.GstState
C.gst_element_get_state(
(*C.GstElement)(e.Instance()),
(*C.GstState)(unsafe.Pointer(&curState)),
(*C.GstState)(unsafe.Pointer(&cState)),
C.GstClockTime(ClockTimeNone),
)
return nil
}
// LinkFiltered wraps gst_element_link_filtered and link this element to the given one
// using the provided sink caps.
func (e *Element) LinkFiltered(elem *Element, caps *Caps) error {
if ok := C.gst_element_link_filtered((*C.GstElement)(e.Instance()), (*C.GstElement)(elem.Instance()), (*C.GstCaps)(caps.Instance())); !gobool(ok) {
return fmt.Errorf("Failed to link %s to %s with provider caps", e.Name(), elem.Name())
// CallAsync calls f from another thread. This is to be used for cases when a state change has to be performed from a streaming
// thread, directly via SetState or indirectly e.g. via SEEK events.
//
// Calling those functions directly from the streaming thread will cause deadlocks in many situations, as they might involve waiting
// for the streaming thread to shut down from this very streaming thread.
func (e *Element) CallAsync(f func()) {
ptr := gopointer.Save(f)
C.gst_element_call_async(
e.Instance(),
C.GstElementCallAsyncFunc(C.cgoElementCallAsync),
(C.gpointer)(unsafe.Pointer(ptr)),
C.GDestroyNotify(C.cgoElementAsyncDestroyNotify),
)
}
// Connect connects to the given signal on this element, and applies f as the callback. The callback must
// match the signature of the expected callback from the documentation. However, instead of specifying C types
// for arguments specify the go-gst equivalent (e.g. *gst.Element for almost all GstElement derivitives).
//
// This and the Emit() method may get moved down the hierarchy to the Object level at some point, since
func (e *Element) Connect(signal string, f interface{}) (glib.SignalHandle, error) {
// Elements are sometimes their own type unique from TYPE_ELEMENT. So make sure a type marshaler
// is registered for whatever this type is. Use the built-in element marshaler.
if e.TypeFromInstance() != glib.Type(C.GST_TYPE_ELEMENT) {
glib.RegisterGValueMarshalers([]glib.TypeMarshaler{{T: e.TypeFromInstance(), F: marshalElement}})
}
return nil
return e.Object.Connect(signal, f, nil)
}
// Emit is a wrapper around g_signal_emitv() and emits the signal specified by the string s to an Object. Arguments to
// callback functions connected to this signal must be specified in args. Emit() returns an interface{} which must be
// type asserted as the Go equivalent type to the return value for native C callback.
//
// Note that this code is unsafe in that the types of values in args are not checked against whether they are suitable
// for the callback.
func (e *Element) Emit(signal string, args ...interface{}) (interface{}, error) {
// We are wrapping this for the same reason as Connect.
if e.TypeFromInstance() != glib.Type(C.GST_TYPE_ELEMENT) {
glib.RegisterGValueMarshalers([]glib.TypeMarshaler{{T: e.TypeFromInstance(), F: marshalElement}})
}
return e.Object.Emit(signal, args...)
}
// Info is a convenience wrapper for posting an info message from inside an element. Only to be used from
// plugins.
func (e *Element) Info(domain Domain, text string) {
function, file, line, _ := runtime.Caller(1)
e.MessageFull(MessageInfo, domain, ErrorCode(0), "", text, path.Base(file), runtime.FuncForPC(function).Name(), line)
}
// Warning is a convenience wrapper for posting a warning message from inside an element. Only to be used from
// plugins.
func (e *Element) Warning(domain Domain, text string) {
function, file, line, _ := runtime.Caller(1)
e.MessageFull(MessageWarning, domain, ErrorCode(0), "", text, path.Base(file), runtime.FuncForPC(function).Name(), line)
}
// Error is a convenience wrapper for posting an error message from inside an element. Only to be used from
// plugins.
func (e *Element) Error(domain Domain, code ErrorCode, text, debug string) {
function, file, line, _ := runtime.Caller(1)
e.MessageFull(MessageError, domain, code, text, debug, path.Base(file), runtime.FuncForPC(function).Name(), line)
}
// MessageFull will post an error, warning, or info message on the bus from inside an element. Only to be used
// from plugins.
func (e *Element) MessageFull(msgType MessageType, domain Domain, code ErrorCode, text, debug, file, function string, line int) {
var cTxt, cDbg unsafe.Pointer
if text != "" {
cTxt = unsafe.Pointer(C.CString(text))
}
if debug != "" {
cDbg = unsafe.Pointer(C.CString(debug))
}
C.gst_element_message_full(
e.Instance(),
C.GstMessageType(msgType),
newQuarkFromString(string(domain)),
C.gint(code),
(*C.gchar)(cTxt),
(*C.gchar)(cDbg),
C.CString(file),
C.CString(function),
C.gint(line),
)
}
// GetBus returns the GstBus for retrieving messages from this element. This function returns
@@ -85,37 +211,6 @@ func (e *Element) GetClock() *Clock {
return wrapClock(&glib.Object{GObject: glib.ToGObject(unsafe.Pointer(cClock))})
}
// GetState returns the current state of this element.
func (e *Element) GetState() State {
return State(e.Instance().current_state)
}
// SetState sets the target state for this element.
func (e *Element) SetState(state State) error {
stateRet := C.gst_element_set_state((*C.GstElement)(e.Instance()), C.GstState(state))
if stateRet == C.GST_STATE_CHANGE_FAILURE {
return fmt.Errorf("Failed to change state to %s", state.String())
}
return nil
}
// BlockSetState is like SetState except it will block until the transition
// is complete.
func (e *Element) BlockSetState(state State) error {
if err := e.SetState(state); err != nil {
return err
}
cState := C.GstState(state)
var curState C.GstState
C.gst_element_get_state(
(*C.GstElement)(e.Instance()),
(*C.GstState)(unsafe.Pointer(&curState)),
(*C.GstState)(unsafe.Pointer(&cState)),
C.GstClockTime(ClockTimeNone),
)
return nil
}
// GetFactory returns the factory that created this element. No refcounting is needed.
func (e *Element) GetFactory() *ElementFactory {
factory := C.gst_element_get_factory((*C.GstElement)(e.Instance()))
@@ -136,18 +231,6 @@ func (e *Element) GetPads() []*Pad {
return out
}
// GetStaticPad retrieves a pad from element by name. This version only retrieves
// already-existing (i.e. 'static') pads.
func (e *Element) GetStaticPad(name string) *Pad {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
pad := C.gst_element_get_static_pad(e.Instance(), (*C.gchar)(unsafe.Pointer(cname)))
if pad == nil {
return nil
}
return wrapPad(toGObject(unsafe.Pointer(pad)))
}
// GetPadTemplates retrieves a list of the pad templates associated with this element.
// The list must not be modified by the calling code.
func (e *Element) GetPadTemplates() []*PadTemplate {
@@ -164,6 +247,23 @@ func (e *Element) GetPadTemplates() []*PadTemplate {
return out
}
// GetState returns the current state of this element.
func (e *Element) GetState() State {
return State(e.Instance().current_state)
}
// GetStaticPad retrieves a pad from element by name. This version only retrieves
// already-existing (i.e. 'static') pads.
func (e *Element) GetStaticPad(name string) *Pad {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
pad := C.gst_element_get_static_pad(e.Instance(), (*C.gchar)(unsafe.Pointer(cname)))
if pad == nil {
return nil
}
return wrapPad(toGObject(unsafe.Pointer(pad)))
}
// Has returns true if this element has the given flags.
func (e *Element) Has(flags ElementFlags) bool {
return gobool(C.gstObjectFlagIsSet(C.toGstObject(e.Unsafe()), C.GstElementFlags(flags)))
@@ -174,34 +274,21 @@ func (e *Element) IsURIHandler() bool {
return gobool(C.gstElementIsURIHandler(e.Instance()))
}
// URIHandler returns a URIHandler interface if implemented by this element. Otherwise it
// returns nil. Currently this only supports elements built through this package, however,
// inner application elements could still use the interface as a reference implementation.
func (e *Element) URIHandler() URIHandler {
if C.toGstURIHandler(e.Unsafe()) == nil {
return nil
// Link wraps gst_element_link and links this element to the given one.
func (e *Element) Link(elem *Element) error {
if ok := C.gst_element_link((*C.GstElement)(e.Instance()), (*C.GstElement)(elem.Instance())); !gobool(ok) {
return fmt.Errorf("Failed to link %s to %s", e.Name(), elem.Name())
}
return &gstURIHandler{ptr: e.Instance()}
return nil
}
// TOCSetter returns a TOCSetter interface if implemented by this element. Otherwise it
// returns nil. Currently this only supports elements built through this package, however,
// inner application elements could still use the interface as a reference implementation.
func (e *Element) TOCSetter() TOCSetter {
if C.toTocSetter(e.Instance()) == nil {
return nil
// LinkFiltered wraps gst_element_link_filtered and link this element to the given one
// using the provided sink caps.
func (e *Element) LinkFiltered(elem *Element, caps *Caps) error {
if ok := C.gst_element_link_filtered((*C.GstElement)(e.Instance()), (*C.GstElement)(elem.Instance()), (*C.GstCaps)(caps.Instance())); !gobool(ok) {
return fmt.Errorf("Failed to link %s to %s with provider caps", e.Name(), elem.Name())
}
return &gstTOCSetter{ptr: e.Instance()}
}
// TagSetter returns a TagSetter interface if implemented by this element. Otherwise it returns nil.
// This currently only supports elements built through this package's bindings, however, inner application
// elements can still implement the interface themselves if they want.
func (e *Element) TagSetter() TagSetter {
if C.toTagSetter(e.Instance()) == nil {
return nil
}
return &gstTagSetter{ptr: e.Instance()}
}
// Query performs a query on the given element.
@@ -253,32 +340,13 @@ func (e *Element) SendEvent(ev *Event) bool {
return gobool(C.gst_element_send_event(e.Instance(), ev.Instance()))
}
// Connect connects to the given signal on this element, and applies f as the callback. The callback must
// match the signature of the expected callback from the documentation. However, instead of specifying C types
// for arguments specify the go-gst equivalent (e.g. *gst.Element for almost all GstElement derivitives).
//
// This and the Emit() method may get moved down the hierarchy to the Object level at some point, since
func (e *Element) Connect(signal string, f interface{}) (glib.SignalHandle, error) {
// Elements are sometimes their own type unique from TYPE_ELEMENT. So make sure a type marshaler
// is registered for whatever this type is. Use the built-in element marshaler.
if e.TypeFromInstance() != glib.Type(C.GST_TYPE_ELEMENT) {
glib.RegisterGValueMarshalers([]glib.TypeMarshaler{{T: e.TypeFromInstance(), F: marshalElement}})
// SetState sets the target state for this element.
func (e *Element) SetState(state State) error {
stateRet := C.gst_element_set_state((*C.GstElement)(e.Instance()), C.GstState(state))
if stateRet == C.GST_STATE_CHANGE_FAILURE {
return fmt.Errorf("Failed to change state to %s", state.String())
}
return e.Object.Connect(signal, f, nil)
}
// Emit is a wrapper around g_signal_emitv() and emits the signal specified by the string s to an Object. Arguments to
// callback functions connected to this signal must be specified in args. Emit() returns an interface{} which must be
// type asserted as the Go equivalent type to the return value for native C callback.
//
// Note that this code is unsafe in that the types of values in args are not checked against whether they are suitable
// for the callback.
func (e *Element) Emit(signal string, args ...interface{}) (interface{}, error) {
// We are wrapping this for the same reason as Connect.
if e.TypeFromInstance() != glib.Type(C.GST_TYPE_ELEMENT) {
glib.RegisterGValueMarshalers([]glib.TypeMarshaler{{T: e.TypeFromInstance(), F: marshalElement}})
}
return e.Object.Emit(signal, args...)
return nil
}
// SyncStateWithParent tries to change the state of the element to the same as its parent. If this function returns
@@ -287,32 +355,32 @@ func (e *Element) SyncStateWithParent() bool {
return gobool(C.gst_element_sync_state_with_parent(e.Instance()))
}
// AbortState aborts the state change of the element. This function is used by elements that do asynchronous state changes
// and find out something is wrong.
func (e *Element) AbortState() { C.gst_element_abort_state(e.Instance()) }
// AddPad adds a pad (link point) to element. pad's parent will be set to element
//
// Pads are automatically activated when added in the PAUSED or PLAYING state.
//
// The pad and the element should be unlocked when calling this function.
//
// This function will emit the pad-added signal on the element.
func (e *Element) AddPad(pad *Pad) bool {
return gobool(C.gst_element_add_pad(e.Instance(), pad.Instance()))
// TOCSetter returns a TOCSetter interface if implemented by this element. Otherwise it
// returns nil. Currently this only supports elements built through this package, however,
// inner application elements could still use the interface as a reference implementation.
func (e *Element) TOCSetter() TOCSetter {
if C.toTocSetter(e.Instance()) == nil {
return nil
}
return &gstTOCSetter{ptr: e.Instance()}
}
// CallAsync calls f from another thread. This is to be used for cases when a state change has to be performed from a streaming
// thread, directly via SetState or indirectly e.g. via SEEK events.
//
// Calling those functions directly from the streaming thread will cause deadlocks in many situations, as they might involve waiting
// for the streaming thread to shut down from this very streaming thread.
func (e *Element) CallAsync(f func()) {
ptr := gopointer.Save(f)
C.gst_element_call_async(
e.Instance(),
C.GstElementCallAsyncFunc(C.cgoElementCallAsync),
(C.gpointer)(unsafe.Pointer(ptr)),
C.GDestroyNotify(C.cgoElementAsyncDestroyNotify),
)
// TagSetter returns a TagSetter interface if implemented by this element. Otherwise it returns nil.
// This currently only supports elements built through this package's bindings, however, inner application
// elements can still implement the interface themselves if they want.
func (e *Element) TagSetter() TagSetter {
if C.toTagSetter(e.Instance()) == nil {
return nil
}
return &gstTagSetter{ptr: e.Instance()}
}
// URIHandler returns a URIHandler interface if implemented by this element. Otherwise it
// returns nil. Currently this only supports elements built through this package, however,
// inner application elements could still use the interface as a reference implementation.
func (e *Element) URIHandler() URIHandler {
if C.toGstURIHandler(e.Unsafe()) == nil {
return nil
}
return &gstURIHandler{ptr: e.Instance()}
}

102
gst/gst_element_class.go Normal file
View File

@@ -0,0 +1,102 @@
package gst
/*
#include "gst.go.h"
*/
import "C"
import "unsafe"
// ElementClass represents the subclass of an element provided by a plugin.
type ElementClass struct{ *ObjectClass }
// Instance returns the underlying GstElementClass instance.
func (e *ElementClass) Instance() *C.GstElementClass {
return C.toGstElementClass(e.Unsafe())
}
// AddMetadata sets key with the given value in the metadata of the class.
func (e *ElementClass) AddMetadata(key, value string) {
C.gst_element_class_add_static_metadata(
e.Instance(),
(*C.gchar)(C.CString(key)),
(*C.gchar)(C.CString(value)),
)
}
// AddPadTemplate adds a padtemplate to an element class. This is mainly used in the
// ClassInit functions of ObjectSubclasses. If a pad template with the same name as an
// already existing one is added the old one is replaced by the new one.
//
// templ's reference count will be incremented, and any floating reference will be removed
func (e *ElementClass) AddPadTemplate(templ *PadTemplate) {
C.gst_element_class_add_pad_template(
e.Instance(),
templ.Instance(),
)
}
// AddStaticPadTemplate adds a pad template to an element class based on the pad template templ. The template
// is first converted to a static pad template.
//
// This is mainly used in the ClassInit functions of element implementations. If a pad template with the
// same name already exists, the old one is replaced by the new one.
func (e *ElementClass) AddStaticPadTemplate(templ *PadTemplate) {
staticTmpl := C.GstStaticPadTemplate{
name_template: templ.Instance().name_template,
direction: templ.Instance().direction,
presence: templ.Instance().presence,
static_caps: C.GstStaticCaps{
caps: templ.Caps().Instance(),
string: C.CString(templ.Name()),
},
}
C.gst_element_class_add_static_pad_template(
e.Instance(),
&staticTmpl,
)
}
// GetMetadata retrieves the metadata associated with key in the class.
func (e *ElementClass) GetMetadata(key string) string {
ckey := C.CString(key)
defer C.free(unsafe.Pointer(ckey))
return C.GoString(C.gst_element_class_get_metadata(e.Instance(), (*C.gchar)(ckey)))
}
// GetPadTemplate retrieves the padtemplate with the given name. No unrefing is necessary.
// If no pad template exists with the given name, nil is returned.
func (e *ElementClass) GetPadTemplate(name string) *PadTemplate {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
tmpl := C.gst_element_class_get_pad_template(e.Instance(), (*C.gchar)(cname))
if tmpl == nil {
return nil
}
return wrapPadTemplate(toGObject(unsafe.Pointer(tmpl)))
}
// GetAllPadTemplates retrieves a slice of all the pad templates associated with this class.
// The list must not be modified.
func (e *ElementClass) GetAllPadTemplates() []*PadTemplate {
glist := C.gst_element_class_get_pad_template_list(e.Instance())
return glistToPadTemplateSlice(glist)
}
// SetMetadata sets the detailed information for this class.
//
// `longname` - The english long name of the element. E.g "File Sink"
//
// `classification` - A string describing the type of element, as an unordered list separated with slashes ('/'). E.g: "Sink/File"
//
// `description` - Sentence describing the purpose of the element. E.g: "Write stream to a file"
//
// `author` - Name and contact details of the author(s). Use \n to separate multiple author metadata. E.g: "Joe Bloggs <joe.blogs at foo.com>"
func (e *ElementClass) SetMetadata(longname, classification, description, author string) {
C.gst_element_class_set_static_metadata(
e.Instance(),
(*C.gchar)(C.CString(longname)),
(*C.gchar)(C.CString(classification)),
(*C.gchar)(C.CString(description)),
(*C.gchar)(C.CString(author)),
)
}

84
gst/gst_errors.go Normal file
View File

@@ -0,0 +1,84 @@
package gst
/*
#include "gst.go.h"
*/
import "C"
// Domain represents the different types of error domains.
type Domain string
// ErrorDomain castings
const (
DomainCore Domain = "CORE"
DomainLibrary Domain = "LIBRARY"
DomainResource Domain = "RESOURCE"
DomainStream Domain = "STREAM"
)
// ErrorCode represents GstGError codes.
type ErrorCode int
// Type castings of CoreErrors
const (
CoreErrorFailed ErrorCode = C.GST_CORE_ERROR_FAILED // (1) a general error which doesn't fit in any other category. Make sure you add a custom message to the error call.
CoreErrorTooLazy ErrorCode = C.GST_CORE_ERROR_TOO_LAZY // (2) do not use this except as a placeholder for deciding where to go while developing code.
CoreErrorNotImplemented ErrorCode = C.GST_CORE_ERROR_NOT_IMPLEMENTED // (3) use this when you do not want to implement this functionality yet.
CoreErrorStateChange ErrorCode = C.GST_CORE_ERROR_STATE_CHANGE // (4) used for state change errors.
CoreErrorPad ErrorCode = C.GST_CORE_ERROR_PAD // (5) used for pad-related errors.
CoreErrorThread ErrorCode = C.GST_CORE_ERROR_THREAD // (6) used for thread-related errors.
CoreErrorNegotiation ErrorCode = C.GST_CORE_ERROR_NEGOTIATION // (7) used for negotiation-related errors.
CoreErrorEvent ErrorCode = C.GST_CORE_ERROR_EVENT // (8) used for event-related errors.
CoreErrorSeek ErrorCode = C.GST_CORE_ERROR_SEEK // (9) used for seek-related errors.
CoreErrorCaps ErrorCode = C.GST_CORE_ERROR_CAPS // (10) used for caps-related errors.
CoreErrorTag ErrorCode = C.GST_CORE_ERROR_TAG // (11) used for negotiation-related errors.
CoreErrorMissingPlugin ErrorCode = C.GST_CORE_ERROR_MISSING_PLUGIN // (12) used if a plugin is missing.
CoreErrorClock ErrorCode = C.GST_CORE_ERROR_CLOCK // (13) used for clock related errors.
CoreErrorDisabled ErrorCode = C.GST_CORE_ERROR_DISABLED // (14) used if functionality has been disabled at compile time.
)
// Type castings for LibraryErrors
const (
LibraryErrorFailed ErrorCode = C.GST_LIBRARY_ERROR_FAILED // (1) a general error which doesn't fit in any other category. Make sure you add a custom message to the error call.
LibraryErrorTooLazy ErrorCode = C.GST_LIBRARY_ERROR_TOO_LAZY // (2) do not use this except as a placeholder for deciding where to go while developing code.
LibraryErrorInit ErrorCode = C.GST_LIBRARY_ERROR_INIT // (3) used when the library could not be opened.
LibraryErrorShutdown ErrorCode = C.GST_LIBRARY_ERROR_SHUTDOWN // (4) used when the library could not be closed.
LibraryErrorSettings ErrorCode = C.GST_LIBRARY_ERROR_SETTINGS // (5) used when the library doesn't accept settings.
LibraryErrorEncode ErrorCode = C.GST_LIBRARY_ERROR_ENCODE // (6) used when the library generated an encoding error.
)
// Type castings for ResourceErrors
const (
ResourceErrorFailed ErrorCode = C.GST_RESOURCE_ERROR_FAILED // (1) a general error which doesn't fit in any other category. Make sure you add a custom message to the error call.
ResourceErrorTooLazy ErrorCode = C.GST_RESOURCE_ERROR_TOO_LAZY // (2) do not use this except as a placeholder for deciding where to go while developing code.
ResourceErrorNotFound ErrorCode = C.GST_RESOURCE_ERROR_NOT_FOUND // (3) used when the resource could not be found.
ResourceErrorBusy ErrorCode = C.GST_RESOURCE_ERROR_BUSY // (4) used when resource is busy.
ResourceErrorOpenRead ErrorCode = C.GST_RESOURCE_ERROR_OPEN_READ // (5) used when resource fails to open for reading.
ResourceErrorOpenWrite ErrorCode = C.GST_RESOURCE_ERROR_OPEN_WRITE // (6) used when resource fails to open for writing.
ResourceErrorOpenReadWrite ErrorCode = C.GST_RESOURCE_ERROR_OPEN_READ_WRITE // (7) used when resource cannot be opened for both reading and writing, or either (but unspecified which).
ResourceErrorClose ErrorCode = C.GST_RESOURCE_ERROR_CLOSE // (8) used when the resource can't be closed.
ResourceErrorRead ErrorCode = C.GST_RESOURCE_ERROR_READ // (9) used when the resource can't be read from.
ResourceErrorWrite ErrorCode = C.GST_RESOURCE_ERROR_WRITE // (10) used when the resource can't be written to.
ResourceErrorSeek ErrorCode = C.GST_RESOURCE_ERROR_SEEK // (11) used when a seek on the resource fails.
ResourceErrorSync ErrorCode = C.GST_RESOURCE_ERROR_SYNC // (12) used when a synchronize on the resource fails.
ResourceErrorSettings ErrorCode = C.GST_RESOURCE_ERROR_SETTINGS // (13) used when settings can't be manipulated on.
ResourceErrorNoSpaceLeft ErrorCode = C.GST_RESOURCE_ERROR_NO_SPACE_LEFT // (14) used when the resource has no space left.
ResourceErrorNotAuthorized ErrorCode = C.GST_RESOURCE_ERROR_NOT_AUTHORIZED // (15) used when the resource can't be opened due to missing authorization. (Since: 1.2.4)
)
// Type castings for StreamErrors
const (
StreamErrorFailed ErrorCode = C.GST_STREAM_ERROR_FAILED // (1) a general error which doesn't fit in any other category. Make sure you add a custom message to the error call.
StreamErrorTooLazy ErrorCode = C.GST_STREAM_ERROR_TOO_LAZY // (2) do not use this except as a placeholder for deciding where to go while developing code.
StreamErrorNotImplemented ErrorCode = C.GST_STREAM_ERROR_NOT_IMPLEMENTED // (3) use this when you do not want to implement this functionality yet.
StreamErrorTypeNotFound ErrorCode = C.GST_STREAM_ERROR_TYPE_NOT_FOUND // (4) used when the element doesn't know the stream's type.
StreamErrorWrongType ErrorCode = C.GST_STREAM_ERROR_WRONG_TYPE // (5) used when the element doesn't handle this type of stream.
StreamErrorCodecNotFound ErrorCode = C.GST_STREAM_ERROR_CODEC_NOT_FOUND // (6) used when there's no codec to handle the stream's type.
StreamErrorDecode ErrorCode = C.GST_STREAM_ERROR_DECODE // (7) used when decoding fails.
StreamErrorEncode ErrorCode = C.GST_STREAM_ERROR_ENCODE // (8) used when encoding fails.
StreamErrorDemux ErrorCode = C.GST_STREAM_ERROR_DEMUX // (9) used when demuxing fails.
StreamErrorMux ErrorCode = C.GST_STREAM_ERROR_MUX // (10) used when muxing fails.
StreamErrorFormat ErrorCode = C.GST_STREAM_ERROR_FORMAT // (11) used when the stream is of the wrong format (for example, wrong caps).
StreamErrorDecrypt ErrorCode = C.GST_STREAM_ERROR_DECRYPT // (12) used when the stream is encrypted and can't be decrypted because this is not supported by the element.
StreamErrorDecryptNoKey ErrorCode = C.GST_STREAM_ERROR_DECRYPT_NOKEY // (13) used when the stream is encrypted and can't be decrypted because no suitable key is available.
)

View File

@@ -15,6 +15,10 @@ type Event struct {
ptr *C.GstEvent
}
// FromGstEventUnsafe wraps the pointer to the given C GstEvent with the go type.
// This is meant for internal usage and is exported for visibility to other packages.
func FromGstEventUnsafe(ev unsafe.Pointer) *Event { return wrapEvent((*C.GstEvent)(ev)) }
// Instance returns the underlying GstEvent instance.
func (e *Event) Instance() *C.GstEvent { return C.toGstEvent(unsafe.Pointer(e.ptr)) }

View File

@@ -79,12 +79,7 @@ func (o *Object) ListProperties() []*ParameterSpec {
C.g_param_spec_sink(prop) // steal the ref on the property
out = append(out, &ParameterSpec{
paramSpec: prop,
Name: C.GoString(C.g_param_spec_get_name(prop)),
Blurb: C.GoString(C.g_param_spec_get_blurb(prop)),
Flags: flags,
ValueType: glib.Type(prop.value_type),
OwnerType: glib.Type(prop.owner_type),
DefaultValue: glib.ValueFromNative(unsafe.Pointer(&gval)),
defaultValue: glib.ValueFromNative(unsafe.Pointer(&gval)),
})
}
return out

View File

@@ -1,11 +1,160 @@
package gst
// #include "gst.go.h"
/*
#include "gst.go.h"
extern gboolean goPluginInit (GstPlugin * plugin, gpointer user_data);
extern gboolean goGlobalPluginInit (GstPlugin * plugin);
gboolean cgoPluginInit (GstPlugin * plugin, gpointer user_data)
{
return goPluginInit(plugin, user_data);
}
gboolean cgoGlobalPluginInit(GstPlugin * plugin)
{
return goGlobalPluginInit(plugin);
}
GstPluginDesc * exportPluginMeta (gint major, gint minor, gchar * name, gchar * description, GstPluginInitFunc init, gchar * version, gchar * license, gchar * source, gchar * package, gchar * origin, gchar * release_datetime)
{
GstPluginDesc * desc = malloc ( sizeof(GstPluginDesc) );
desc->major_version = major;
desc->minor_version = minor;
desc->name = name;
desc->description = description;
desc->plugin_init = init;
desc->version = version;
desc->license = license;
desc->source = source;
desc->package = package;
desc->origin = origin;
desc->release_datetime = release_datetime;
return desc;
}
*/
import "C"
import (
"errors"
"unsafe"
"github.com/gotk3/gotk3/glib"
gopointer "github.com/mattn/go-pointer"
)
// PluginMetadata represents the information to include when registering a new plugin
// with gstreamer.
type PluginMetadata struct {
// The major version number of the GStreamer core that the plugin was compiled for, you can just use VersionMajor here
MajorVersion Version
// The minor version number of the GStreamer core that the plugin was compiled for, you can just use VersionMinor here
MinorVersion Version
// A unique name of the plugin (ideally prefixed with an application- or library-specific namespace prefix in order to
// avoid name conflicts in case a similar plugin with the same name ever gets added to GStreamer)
Name string
// A description of the plugin
Description string
// The function to call when initiliazing the plugin
Init PluginInitFunc
// The version of the plugin
Version string
// The license for the plugin, must match one of the license constants in this package
License License
// The source module the plugin belongs to
Source string
// The shipped package the plugin belongs to
Package string
// The URL to the provider of the plugin
Origin string
// The date of release in ISO 8601 format.
// See https://gstreamer.freedesktop.org/documentation/gstreamer/gstplugin.html?gi-language=c#GstPluginDesc for more details.
ReleaseDate string
}
var globalPluginInit PluginInitFunc
// Export will export the PluginMetadata to an unsafe pointer to a GstPluginDesc.
func (p *PluginMetadata) Export() unsafe.Pointer {
globalPluginInit = p.Init
desc := C.exportPluginMeta(
C.gint(p.MajorVersion),
C.gint(p.MinorVersion),
(*C.gchar)(C.CString(p.Name)),
(*C.gchar)(C.CString(p.Description)),
(C.GstPluginInitFunc(C.cgoGlobalPluginInit)),
(*C.gchar)(C.CString(p.Version)),
(*C.gchar)(C.CString(string(p.License))),
(*C.gchar)(C.CString(p.Source)),
(*C.gchar)(C.CString(p.Package)),
(*C.gchar)(C.CString(p.Origin)),
(*C.gchar)(C.CString(p.ReleaseDate)),
)
return unsafe.Pointer(desc)
}
// PluginInitFunc is a function called by the plugin loader at startup. This function should register
// all the features of the plugin. The function should return true if the plugin is initialized successfully.
type PluginInitFunc func(*Plugin) bool
// Plugin is a go representation of a GstPlugin.
type Plugin struct{ *Object }
// RegisterPlugin will register a static plugin, i.e. a plugin which is private to an application
// or library and contained within the application or library (as opposed to being shipped as a
// separate module file).
func RegisterPlugin(desc *PluginMetadata, initFunc PluginInitFunc) bool {
cName := C.CString(desc.Name)
cDesc := C.CString(desc.Description)
cVers := C.CString(desc.Version)
cLics := C.CString(string(desc.License))
cSrc := C.CString(desc.Source)
cPkg := C.CString(desc.Package)
cOrg := C.CString(desc.Origin)
defer func() {
for _, ptr := range []*C.char{cName, cDesc, cVers, cLics, cSrc, cPkg, cOrg} {
C.free(unsafe.Pointer(ptr))
}
}()
fPtr := gopointer.Save(initFunc)
return gobool(C.gst_plugin_register_static_full(
C.gint(desc.MajorVersion), C.gint(desc.MinorVersion),
(*C.gchar)(cName), (*C.gchar)(cDesc),
C.GstPluginInitFullFunc(C.cgoPluginInit),
(*C.gchar)(cVers), (*C.gchar)(cLics),
(*C.gchar)(cSrc), (*C.gchar)(cPkg),
(*C.gchar)(cOrg), (C.gpointer)(unsafe.Pointer(fPtr)),
))
}
// LoadPluginByName loads the named plugin and places a ref count on it. The function
// returns nil if the plugin could not be loaded.
func LoadPluginByName(name string) *Plugin {
cstr := C.CString(name)
defer C.free(unsafe.Pointer(cstr))
plugin := C.gst_plugin_load_by_name((*C.gchar)(unsafe.Pointer(cstr)))
if plugin == nil {
return nil
}
return wrapPlugin(&glib.Object{GObject: glib.ToGObject(unsafe.Pointer(plugin))})
}
// LoadPluginFile loads the given plugin and refs it. If an error is returned Plugin will be nil.
func LoadPluginFile(fpath string) (*Plugin, error) {
cstr := C.CString(fpath)
defer C.free(unsafe.Pointer(cstr))
var gerr *C.GError
plugin := C.gst_plugin_load_file((*C.gchar)(unsafe.Pointer(cstr)), (**C.GError)(&gerr))
if gerr != nil {
defer C.g_free((C.gpointer)(gerr))
return nil, errors.New(C.GoString(gerr.message))
}
return wrapPlugin(&glib.Object{GObject: glib.ToGObject(unsafe.Pointer(plugin))}), nil
}
// Instance returns the underlying GstPlugin instance.
func (p *Plugin) Instance() *C.GstPlugin { return C.toGstPlugin(p.Unsafe()) }
@@ -37,12 +186,12 @@ func (p *Plugin) Version() string {
}
// License returns the license for this plugin.
func (p *Plugin) License() string {
func (p *Plugin) License() License {
ret := C.gst_plugin_get_license((*C.GstPlugin)(p.Instance()))
if ret == nil {
return ""
}
return C.GoString(ret)
return License(C.GoString(ret))
}
// Source returns the source module for this plugin.

View File

@@ -14,6 +14,10 @@ type Query struct {
ptr *C.GstQuery
}
// FromGstQueryUnsafe wraps the pointer to the given C GstQuery with the go type.
// This is meant for internal usage and is exported for visibility to other packages.
func FromGstQueryUnsafe(query unsafe.Pointer) *Query { return wrapQuery((*C.GstQuery)(query)) }
// NewAcceptCapsQuery constructs a new query object for querying if caps are accepted.
func NewAcceptCapsQuery(caps *Caps) *Query {
return wrapQuery(C.gst_query_new_accept_caps(caps.Instance()))

View File

@@ -2,6 +2,7 @@ package gst
// #include "gst.go.h"
import "C"
import "unsafe"
// Segment is a go wrapper around a GstSegment.
// See: https://gstreamer.freedesktop.org/documentation/gstreamer/gstsegment.html?gi-language=c#GstSegment
@@ -9,6 +10,12 @@ type Segment struct {
ptr *C.GstSegment
}
// FromGstSegmentUnsafe wraps the given C GstSegment in the go type. It is meant for internal usage
// and exported for visibilty to other packages.
func FromGstSegmentUnsafe(segment unsafe.Pointer) *Segment {
return wrapSegment((*C.GstSegment)(segment))
}
// NewSegment allocates and initializes a new Segment.
func NewSegment() *Segment {
return wrapSegment(C.gst_segment_new())

View File

@@ -1,6 +1,8 @@
package gst
// #include "gst.go.h"
/*
#include "gst.go.h"
*/
import "C"
import (
"errors"
@@ -10,7 +12,8 @@ import (
)
// InterfaceURIHandler represents the GstURIHandler interface GType. Use this when querying bins
// for elements that implement a URIHandler.
// for elements that implement a URIHandler, or when signaling that a GoElement provides this
// interface.
var InterfaceURIHandler = glib.Type(C.GST_TYPE_URI_HANDLER)
// URIHandler represents an interface that elements can implement to provide URI handling
@@ -20,13 +23,13 @@ type URIHandler interface {
GetURI() string
// GetURIType returns the type of URI this element can handle.
GetURIType() URIType
// GetURIProtocols returns the protocols this element can handle.
GetURIProtocols() []string
// GetProtocols returns the protocols this element can handle.
GetProtocols() []string
// SetURI tries to set the URI of the given handler.
SetURI(string) (bool, error)
}
// gstURIHandler implements a URIHandler that is backed by an Element from the C runtime.
// gstURIHandler implements a URIHandler that is backed by an Element from the C API.
type gstURIHandler struct {
ptr *C.GstElement
}
@@ -48,8 +51,8 @@ func (g *gstURIHandler) GetURIType() URIType {
return URIType(ty)
}
// GetURIProtocols returns the protocols this element can handle.
func (g *gstURIHandler) GetURIProtocols() []string {
// GetProtocols returns the protocols this element can handle.
func (g *gstURIHandler) GetProtocols() []string {
protocols := C.gst_uri_handler_get_protocols((*C.GstURIHandler)(g.Instance()))
if protocols == nil {
return nil

View File

@@ -209,6 +209,10 @@ func wrapAllocationParams(obj *C.GstAllocationParams) *AllocationParams {
return &AllocationParams{ptr: obj}
}
func wrapElementClass(klass C.gpointer) *ElementClass {
return &ElementClass{&ObjectClass{ptr: C.toGObjectClass(unsafe.Pointer(klass))}}
}
// Clock wrappers
func clockTimeToDuration(n ClockTime) time.Duration {

158
gst/interfaces.go Normal file
View File

@@ -0,0 +1,158 @@
package gst
/*
#include "gst.go.h"
extern void goClassInit (gpointer g_class, gpointer class_data);
extern void goInstanceInit (GTypeInstance * instance, gpointer g_class);
extern void goObjectSetProperty (GObject * object, guint property_id, const GValue * value, GParamSpec *pspec);
extern void goObjectGetProperty (GObject * object, guint property_id, GValue * value, GParamSpec * pspec);
extern void goObjectConstructed (GObject * object);
extern void goObjectFinalize (GObject * object, gpointer klass);
void objectFinalize (GObject * object)
{
GObjectClass *parent = g_type_class_peek_parent((G_OBJECT_GET_CLASS(object)));
goObjectFinalize(object, G_OBJECT_GET_CLASS(object));
parent->finalize(object);
}
void objectConstructed (GObject * object)
{
GObjectClass *parent = g_type_class_peek_parent((G_OBJECT_GET_CLASS(object)));
goObjectConstructed(object);
parent->constructed(object);
}
void cgoClassInit (gpointer g_class, gpointer class_data)
{
((GObjectClass *)g_class)->set_property = goObjectSetProperty;
((GObjectClass *)g_class)->get_property = goObjectGetProperty;
((GObjectClass *)g_class)->constructed = objectConstructed;
((GObjectClass *)g_class)->finalize = objectFinalize;
goClassInit(g_class, class_data);
}
void cgoInstanceInit (GTypeInstance * instance, gpointer g_class)
{
goInstanceInit(instance, g_class);
}
*/
import "C"
import (
"reflect"
"unsafe"
"github.com/gotk3/gotk3/glib"
gopointer "github.com/mattn/go-pointer"
)
// Extendable is an interface implemented by extendable classes. It provides
// the methods necessary to setup the vmethods on the object it represents.
type Extendable interface {
Type() glib.Type
ClassSize() int64
InstanceSize() int64
InitClass(unsafe.Pointer, GoElement)
}
type extendElement struct{}
func (e *extendElement) Type() glib.Type { return glib.Type(C.gst_element_get_type()) }
func (e *extendElement) ClassSize() int64 { return int64(C.sizeof_GstElementClass) }
func (e *extendElement) InstanceSize() int64 { return int64(C.sizeof_GstElement) }
func (e *extendElement) InitClass(klass unsafe.Pointer, elem GoElement) {}
// ExtendsElement signifies a GoElement that extends a GstElement.
var ExtendsElement Extendable = &extendElement{}
// GoElement is an interface to be implemented by GStreamer elements built using the
// go bindings. The various methods are called throughout the lifecycle of the plugin.
type GoElement interface {
GoObjectSubclass
GoObject
}
// privateFromObj returns the actual value of the address we stored in the object's private data.
func privateFromObj(obj unsafe.Pointer) unsafe.Pointer {
private := C.g_type_instance_get_private((*C.GTypeInstance)(obj), C.objectGType((*C.GObject)(obj)))
privAddr := (*unsafe.Pointer)(unsafe.Pointer(private))
return *privAddr
}
// FromObjectUnsafePrivate will return the GoElement addressed in the private data of the given GObject.
func FromObjectUnsafePrivate(obj unsafe.Pointer) GoElement {
ptr := gopointer.Restore(privateFromObj(obj))
return ptr.(GoElement)
}
// GoObjectSubclass is an interface that abstracts on the GObjectClass. It should be implemented
// by plugins using the go bindings.
type GoObjectSubclass interface {
// New should return a new instantiated GoElement ready to be used.
New() GoElement
// TypeInit is called after the GType is registered and right before ClassInit. It is when the
// element should add any interfaces it plans to implement.
TypeInit(*TypeInstance)
// ClassInit is called on the element after registering it with GStreamer. This is when the element
// should install any properties and pad templates it has.
ClassInit(*ElementClass)
}
// GoObject is an interface that abstracts on the GObject. It should be implemented by plugins using
// the gobindings.
type GoObject interface {
// SetProperty should set the value of the property with the given id. ID is the index+1 of the parameter
// in the order it was registered.
SetProperty(obj *Object, id uint, value *glib.Value)
// GetProperty should retrieve the value of the property with the given id. ID is the index+1 of the parameter
// in the order it was registered.
GetProperty(obj *Object, id uint) *glib.Value
// Constructed is called when the Object has finished setting up.
Constructed(*Object)
}
type classData struct {
elem GoElement
ext Extendable
}
func gtypeForGoElement(name string, elem GoElement, extendable Extendable) C.GType {
registerMutex.Lock()
defer registerMutex.Unlock()
// fmt.Printf("Checking registration of %v\n", reflect.TypeOf(elem).String())
if registered, ok := registeredTypes[reflect.TypeOf(elem).String()]; ok {
return registered
}
classData := &classData{
elem: elem,
ext: extendable,
}
ptr := gopointer.Save(classData)
typeInfo := C.GTypeInfo{
class_size: C.gushort(extendable.ClassSize()),
base_init: nil,
base_finalize: nil,
class_init: C.GClassInitFunc(C.cgoClassInit),
class_finalize: nil,
class_data: (C.gconstpointer)(ptr),
instance_size: C.gushort(extendable.InstanceSize()),
n_preallocs: 0,
instance_init: C.GInstanceInitFunc(C.cgoInstanceInit),
value_table: nil,
}
gtype := C.g_type_register_static(
C.GType(extendable.Type()),
(*C.gchar)(C.CString(name)),
&typeInfo,
C.GTypeFlags(0),
)
elem.TypeInit(&TypeInstance{gtype: gtype, gotype: elem})
// fmt.Printf("Registering %v to type %v\n", reflect.TypeOf(elem).String(), gtype)
registeredTypes[reflect.TypeOf(elem).String()] = gtype
return gtype
}

11
gst/register.go Normal file
View File

@@ -0,0 +1,11 @@
package gst
// #include "gst.go.h"
import "C"
import "sync"
var registerMutex sync.RWMutex
var registeredTypes = make(map[string]C.GType)
var registeredClasses = make(map[C.gpointer]GoElement)
var globalURIHdlr URIHandler