add GstBaseSink and filesink example

This commit is contained in:
Avi Zimmerman
2021-01-08 22:00:38 +02:00
parent ca1cdb00e7
commit 8d5a0bbf38
19 changed files with 1261 additions and 19 deletions
+258
View File
@@ -0,0 +1,258 @@
package main
import (
"errors"
"fmt"
"io"
"os"
"strings"
"github.com/tinyzimmer/go-glib/glib"
"github.com/tinyzimmer/go-gst/gst"
"github.com/tinyzimmer/go-gst/gst/base"
)
// CAT is the log category for the gofilesink. It is safe to define GStreamer objects as globals
// without calling gst.Init, since in the context of a loaded plugin all initialization has
// already been taken care of by the loading application.
var CAT = gst.NewDebugCategory(
"gofilesink",
gst.DebugColorNone,
"GoFileSink Element",
)
// 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 write to.
// 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 to write the file to", // 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 writing to
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 fileSink struct {
// The settings for the element
settings *settings
// The current state of the element
state *state
}
// setLocation is a simple method to check the validity of a provided file path and set the
// local value with it.
func (f *fileSink) setLocation(path string) error {
if f.state.started {
return errors.New("Changing the `location` property on a started `GoFileSink` is not supported")
}
f.settings.location = strings.TrimPrefix(path, "file://") // should obviously use url.URL and do actual parsing
return nil
}
// The ObjectSubclass implementations below are for registering the various aspects of our
// element and its capabilities with the type system. These are the minimum methods that
// should be implemented by an element.
// Every element needs to provide its own constructor that returns an initialized
// gst.GoElement implementation. Here we simply create a new fileSink with zeroed settings
// and state objects.
func (f *fileSink) New() gst.GoElement {
CAT.Log(gst.LevelLog, "Initializing new fileSink object")
return &fileSink{
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 *fileSink) TypeInit(instance *gst.TypeInstance) {
CAT.Log(gst.LevelLog, "Adding URIHandler interface to type")
instance.AddInterface(gst.InterfaceURIHandler)
}
// The ClassInit method should specify the metadata for this element and add any pad templates
// and properties.
func (f *fileSink) ClassInit(klass *gst.ElementClass) {
CAT.Log(gst.LevelLog, "Initializing gofilesink class")
klass.SetMetadata(
"File Sink",
"Sink/File",
"Write stream to a file",
"Avi Zimmerman <avi.zimmerman@gmail.com>",
)
CAT.Log(gst.LevelLog, "Adding sink pad template and properties to class")
klass.AddPadTemplate(gst.NewPadTemplate(
"sink",
gst.PadDirectionSink,
gst.PadPresenceAlways,
gst.NewAnyCaps(),
))
klass.InstallProperties(properties)
}
// Object implementations are used during the initialization of an element. The
// methods are called once the object is constructed and its properties are read
// and written to. These and the rest of the methods described below are documented
// in interfaces in the bindings, however only individual methods needs from those
// interfaces need to be implemented. When left unimplemented, the behavior of the parent
// class is inherited.
// 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 *fileSink) 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).ErrorMessage(gst.DomainLibrary, gst.LibraryErrorSettings,
fmt.Sprintf("Could not set location on object: %s", err.Error()),
"",
)
return
}
self.Log(CAT, gst.LevelInfo, 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 *fileSink) 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).ErrorMessage(gst.DomainLibrary, gst.LibraryErrorFailed,
fmt.Sprintf("Could not convert %s to GValue", f.settings.location),
err.Error(),
)
}
return nil
}
// GstBaseSink implementations are optional methods to implement from the base.GstBaseSinkImpl interface.
// If the method is not overridden by the implementing struct, it will be inherited from the parent class.
// Start is called to start the filesink. Open the file for writing and set the internal state.
func (f *fileSink) Start(self *base.GstBaseSink) bool {
if f.state.started {
self.ErrorMessage(gst.DomainResource, gst.ResourceErrorSettings, "GoFileSink is already started", "")
return false
}
if f.settings.location == "" {
self.ErrorMessage(gst.DomainResource, gst.ResourceErrorSettings, "No location configured on the filesink", "")
return false
}
destFile := f.settings.location
var err error
f.state.file, err = os.Create(destFile)
if err != nil {
self.ErrorMessage(gst.DomainResource, gst.ResourceErrorOpenWrite,
fmt.Sprintf("Could not open %s for writing", destFile), err.Error())
return false
}
self.Log(CAT, gst.LevelDebug, fmt.Sprintf("Opened file %s for writing", destFile))
f.state.started = true
self.Log(CAT, gst.LevelInfo, "GoFileSink has started")
return true
}
// Stop is called to stop the element. Set the internal state and close the file.
func (f *fileSink) Stop(self *base.GstBaseSink) bool {
if !f.state.started {
self.ErrorMessage(gst.DomainResource, gst.ResourceErrorSettings, "GoFileSink is not started", "")
return false
}
if err := f.state.file.Close(); err != nil {
self.ErrorMessage(gst.DomainResource, gst.ResourceErrorWrite, "Failed to close the destination file", err.Error())
return false
}
self.Log(CAT, gst.LevelInfo, "GoFileSink has stopped")
return true
}
// Render is called when a buffer is ready to be written to the file.
func (f *fileSink) Render(self *base.GstBaseSink, buffer *gst.Buffer) gst.FlowReturn {
if !f.state.started {
self.ErrorMessage(gst.DomainResource, gst.ResourceErrorSettings, "GoFileSink is not started", "")
return gst.FlowError
}
self.Log(CAT, gst.LevelTrace, fmt.Sprintf("Rendering buffer at %v", buffer.Instance()))
newPos, err := io.Copy(f.state.file, buffer.Reader())
if err != nil {
self.ErrorMessage(gst.DomainResource, gst.ResourceErrorWrite, "Error copying buffer to file", err.Error())
return gst.FlowError
}
f.state.position += uint64(newPos)
self.Log(CAT, gst.LevelTrace, fmt.Sprintf("New position in file: %v", f.state.position))
return gst.FlowOK
}
// URIHandler implementations are the methods required by the GstURIHandler interface.
// GetURI returns the currently configured URI
func (f *fileSink) GetURI() string { return fmt.Sprintf("file://%s", f.settings.location) }
// GetURIType returns the types of URI this element supports.
func (f *fileSink) GetURIType() gst.URIType { return gst.URISource }
// GetProtocols returns the protcols this element supports.
func (f *fileSink) GetProtocols() []string { return []string{"file"} }
// SetURI should set the URI that this element is working on.
func (f *fileSink) SetURI(uri string) (bool, error) {
if uri == "file://" {
return true, nil
}
err := f.setLocation(uri)
if err != nil {
return false, err
}
CAT.Log(gst.LevelInfo, fmt.Sprintf("Set `location` to %s via URIHandler", f.settings.location))
return true, nil
}
+46
View File
@@ -0,0 +1,46 @@
// The contents of this file could be generated from markers placed in filesink.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-filesink-plugin",
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,
"gofilesink", // The name of the element
gst.RankNone, // The rank of the element
&fileSink{}, // The GoElement implementation for the element
base.ExtendsBaseSink, // 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_gofilesink_get_desc
func gst_plugin_gofilesink_get_desc() unsafe.Pointer { return pluginMeta.Export() }
// main is left unimplemented since these files are compiled to c-shared.
func main() {}
-5
View File
@@ -78,11 +78,6 @@ type fileSrc struct {
state *state
}
func (f *fileSrc) PostMessage(self *gst.Element, msg *gst.Message) bool {
fmt.Println("Received message on the pipeline", msg)
return self.ParentPostMessage(msg)
}
// 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
+1 -1
View File
@@ -14,7 +14,7 @@ import (
var pluginMeta = &gst.PluginMetadata{
MajorVersion: gst.VersionMajor,
MinorVersion: gst.VersionMinor,
Name: "go-file-plugins",
Name: "go-filesrc-plugin",
Description: "File plugins written in Go",
Version: "v0.0.1",
License: gst.LicenseLGPL,
-2
View File
@@ -1,6 +1,4 @@
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=
github.com/tinyzimmer/go-glib v0.0.1 h1:pfsr7EbP23/cZPGlEpsmwtQXjdzYj0S+WhUgOALdsQI=
github.com/tinyzimmer/go-glib v0.0.1/go.mod h1:D5rd0CvYn1p7TBhwlwnBXHSr4d8lwEY9JImPQ66S+Bs=
github.com/tinyzimmer/go-glib v0.0.2 h1:hdvjwrhcS6WrMMeqfxsf3e7/lOhV8gbVyJ9/sN3LfyY=
github.com/tinyzimmer/go-glib v0.0.2/go.mod h1:D5rd0CvYn1p7TBhwlwnBXHSr4d8lwEY9JImPQ66S+Bs=
+3
View File
@@ -1,2 +1,5 @@
// Package base contains bindings for extendable GStreamer base objects.
//
// The objects and methods provided by this package are mostly for use in building
// plugins using the go-gst bindings.
package base
+6 -2
View File
@@ -2,8 +2,12 @@
#define __GST_BASE_GO_H__
#include <gst/base/gstbasesrc.h>
#include <gst/base/gstbasesink.h>
inline GstBaseSrc * toGstBaseSrc (void *p) { return GST_BASE_SRC_CAST(p); };
inline GstBaseSrcClass * toGstBaseSrcClass (void *p) { return (GstBaseSrcClass *)p; };
inline GstBaseSink * toGstBaseSink (void *p) { return GST_BASE_SINK_CAST(p); }
inline GstBaseSrc * toGstBaseSrc (void *p) { return GST_BASE_SRC_CAST(p); }
inline GstBaseSinkClass * toGstBaseSinkClass (void *p) { return (GstBaseSinkClass *)p; }
inline GstBaseSrcClass * toGstBaseSrcClass (void *p) { return (GstBaseSrcClass *)p; }
#endif
+306
View File
@@ -0,0 +1,306 @@
package base
/*
#include "gst.go.h"
*/
import "C"
import (
"time"
"unsafe"
"github.com/tinyzimmer/go-gst/gst"
)
// GstBaseSink represents a GstBaseSink.
type GstBaseSink struct{ *gst.Element }
// ToGstBaseSink returns a GstBaseSink object for the given object.
func ToGstBaseSink(obj *gst.Object) *GstBaseSink {
return &GstBaseSink{&gst.Element{Object: obj}}
}
// wrapGstBaseSink wraps the given unsafe.Pointer in a GstBaseSink instance.
func wrapGstBaseSink(obj *C.GstBaseSink) *GstBaseSink {
return &GstBaseSink{gst.FromGstElementUnsafe(unsafe.Pointer(obj))}
}
// Instance returns the underlying C GstBaseSrc instance
func (g *GstBaseSink) Instance() *C.GstBaseSink {
return C.toGstBaseSink(g.Unsafe())
}
// DoPreroll is for if the sink spawns its own thread for pulling buffers from upstream.
// It should call this method after it has pulled a buffer. If the element needed to preroll,
// this function will perform the preroll and will then block until the element state is changed.
//
// This function should be called with the PREROLL_LOCK held and the object that caused the preroll.
//
// Since the object will always be a gst.MiniObject (which is not implemented properly), this method will check
// against the provided types for structs known to be used in this context. The currently known options
// are events, messages, queries, structures, and buffers. If you come across a need to use this function with
// an unsupported type, feel free to raise an Issue or open a PR.
func (g *GstBaseSink) DoPreroll(obj interface{}) gst.FlowReturn {
miniobj := getPrerollObj(obj)
if miniobj == nil {
return gst.FlowError
}
return gst.FlowReturn(C.gst_base_sink_do_preroll(g.Instance(), miniobj))
}
func getPrerollObj(obj interface{}) *C.GstMiniObject {
switch obj.(type) {
case *gst.Event:
return (*C.GstMiniObject)(unsafe.Pointer(obj.(*gst.Event).Instance()))
case *gst.Buffer:
return (*C.GstMiniObject)(unsafe.Pointer(obj.(*gst.Buffer).Instance()))
case *gst.Message:
return (*C.GstMiniObject)(unsafe.Pointer(obj.(*gst.Message).Instance()))
case *gst.Query:
return (*C.GstMiniObject)(unsafe.Pointer(obj.(*gst.Query).Instance()))
case *gst.Structure:
return (*C.GstMiniObject)(unsafe.Pointer(obj.(*gst.Structure).Instance()))
default:
return nil
}
}
// GetBlocksize gets the number of bytes that the sink will pull when it is operating in pull mode.
func (g *GstBaseSink) GetBlocksize() uint { return uint(C.gst_base_sink_get_blocksize(g.Instance())) }
// GetDropOutOfSegment checks if sink is currently configured to drop buffers which are outside the current segment
func (g *GstBaseSink) GetDropOutOfSegment() bool {
return gobool(C.gst_base_sink_get_drop_out_of_segment(g.Instance()))
}
// GetLastSample gets the last sample that arrived in the sink and was used for preroll or for rendering.
// This property can be used to generate thumbnails.
//
// The GstCaps on the sample can be used to determine the type of the buffer. Unref after usage. Sample will
// be nil if no buffer has arrived yet.
func (g *GstBaseSink) GetLastSample() *gst.Sample {
sample := C.gst_base_sink_get_last_sample(g.Instance())
if sample == nil {
return nil
}
return gst.FromGstSampleUnsafe(unsafe.Pointer(sample))
}
// GetLatency gets the currently configured latency.
func (g *GstBaseSink) GetLatency() time.Duration {
return time.Duration(C.gst_base_sink_get_latency(g.Instance()))
}
// GetMaxBitrate gets the maximum amount of bits per second the sink will render.
func (g *GstBaseSink) GetMaxBitrate() uint64 {
return uint64(C.gst_base_sink_get_max_bitrate(g.Instance()))
}
// GetMaxLateness gets the max lateness value.
func (g *GstBaseSink) GetMaxLateness() int64 {
return int64(C.gst_base_sink_get_max_lateness(g.Instance()))
}
// GetProcessingDeadline gets the processing deadline of the sink.
func (g *GstBaseSink) GetProcessingDeadline() time.Duration {
return time.Duration(C.gst_base_sink_get_processing_deadline(g.Instance()))
}
// GetRenderDelay gets the render delay for the sink.
func (g *GstBaseSink) GetRenderDelay() time.Duration {
return time.Duration(C.gst_base_sink_get_render_delay(g.Instance()))
}
// SinkStats represents the current statistics on a GstBaseSink.
type SinkStats struct {
AverageRate float64
Dropped uint64
Rendered uint64
}
// GetSinkStats returns various GstBaseSink statistics.
func (g *GstBaseSink) GetSinkStats() *SinkStats {
st := gst.FromGstStructureUnsafe(unsafe.Pointer(C.gst_base_sink_get_stats(g.Instance())))
stats := &SinkStats{}
if avgRate, err := st.GetValue("average-rate"); err == nil {
stats.AverageRate = avgRate.(float64)
}
if dropped, err := st.GetValue("dropped"); err == nil {
stats.Dropped = dropped.(uint64)
}
if rendered, err := st.GetValue("rendered"); err == nil {
stats.Rendered = rendered.(uint64)
}
return stats
}
// GetSync checks if the sink is currently configured to synchronize on the clock.
func (g *GstBaseSink) GetSync() bool { return gobool(C.gst_base_sink_get_sync(g.Instance())) }
// GetThrottleTime gets the time that will be inserted between frames to control maximum buffers
// per second.
func (g *GstBaseSink) GetThrottleTime() uint64 {
return uint64(C.gst_base_sink_get_throttle_time(g.Instance()))
}
// GetTsOffset gets the synchronization offset of sink.
func (g *GstBaseSink) GetTsOffset() time.Duration {
return time.Duration(C.gst_base_sink_get_ts_offset(g.Instance()))
}
// IsAsyncEnabled checks if the sink is currently configured to perform asynchronous state changes to PAUSED.
func (g *GstBaseSink) IsAsyncEnabled() bool {
return gobool(C.gst_base_sink_is_async_enabled(g.Instance()))
}
// IsLastSampleEnabled checks if the sink is currently configured to store the last received sample.
func (g *GstBaseSink) IsLastSampleEnabled() bool {
return gobool(C.gst_base_sink_is_last_sample_enabled(g.Instance()))
}
// IsQoSEnabled checks if sink is currently configured to send QoS events upstream.
func (g *GstBaseSink) IsQoSEnabled() bool {
return gobool(C.gst_base_sink_is_qos_enabled(g.Instance()))
}
// QueryLatency queries the sink for the latency parameters. The latency will be queried from the
// upstream elements. live will be TRUE if sink is configured to synchronize against the clock.
// upstreamLive will be TRUE if an upstream element is live.
//
// If both live and upstreamLive are TRUE, the sink will want to compensate for the latency introduced
// by the upstream elements by setting the minLatency to a strictly positive value.
//
// This function is mostly used by subclasses.
func (g *GstBaseSink) QueryLatency() (ok, live, upstreamLive bool, minLatency, maxLatency time.Duration) {
var glive, gupLive C.gboolean
var gmin, gmax C.GstClockTime
ret := C.gst_base_sink_query_latency(g.Instance(), &glive, &gupLive, &gmin, &gmax)
return gobool(ret), gobool(glive), gobool(gupLive), time.Duration(gmin), time.Duration(gmax)
}
// SetAsyncEnabled configures sink to perform all state changes asynchronously. When async is disabled,
// the sink will immediately go to PAUSED instead of waiting for a preroll buffer. This feature is useful
// if the sink does not synchronize against the clock or when it is dealing with sparse streams.
func (g *GstBaseSink) SetAsyncEnabled(enabled bool) {
C.gst_base_sink_set_async_enabled(g.Instance(), gboolean(enabled))
}
// SetBlocksize sets the number of bytes this sink will pull when operating in pull mode.
func (g *GstBaseSink) SetBlocksize(blocksize uint) {
C.gst_base_sink_set_blocksize(g.Instance(), C.guint(blocksize))
}
// SetDropOutOfSegment configures sink to drop buffers which are outside the current segment.
func (g *GstBaseSink) SetDropOutOfSegment(drop bool) {
C.gst_base_sink_set_drop_out_of_segment(g.Instance(), gboolean(drop))
}
// SetLastSampleEnabled configures the sink to store the last received sample.
func (g *GstBaseSink) SetLastSampleEnabled(enabled bool) {
C.gst_base_sink_set_last_sample_enabled(g.Instance(), gboolean(enabled))
}
// SetMaxBitrate sets the maximum amount of bits per second the sink will render.
func (g *GstBaseSink) SetMaxBitrate(bitrate uint64) {
C.gst_base_sink_set_max_bitrate(g.Instance(), C.guint64(bitrate))
}
// SetMaxLateness sets the new max lateness value to max_lateness. This value is used to decide if
// a buffer should be dropped or not based on the buffer timestamp and the current clock time. A
// value of -1 means an unlimited time.
func (g *GstBaseSink) SetMaxLateness(maxLateness int64) {
C.gst_base_sink_set_max_lateness(g.Instance(), C.gint64(maxLateness))
}
// SetProcessingDeadline sets the maximum amount of time (in nanoseconds) that the pipeline can take
// for processing the buffer. This is added to the latency of live pipelines.
//
// This function is usually called by subclasses.
func (g *GstBaseSink) SetProcessingDeadline(deadline time.Duration) {
C.gst_base_sink_set_processing_deadline(g.Instance(), C.GstClockTime(deadline.Nanoseconds()))
}
// SetQoSEnabled configures sink to send Quality-of-Service events upstream.
func (g *GstBaseSink) SetQoSEnabled(enabled bool) {
C.gst_base_sink_set_qos_enabled(g.Instance(), gboolean(enabled))
}
// SetRenderDelay sets the render delay in sink to delay. The render delay is the time between actual
// rendering of a buffer and its synchronisation time. Some devices might delay media rendering which
// can be compensated for with this function.
//
// After calling this function, this sink will report additional latency and other sinks will adjust
// their latency to delay the rendering of their media.
//
// This function is usually called by subclasses.
func (g *GstBaseSink) SetRenderDelay(delay time.Duration) {
C.gst_base_sink_set_render_delay(g.Instance(), C.GstClockTime(delay.Nanoseconds()))
}
// SetSync configures sink to synchronize on the clock or not. When sync is FALSE, incoming samples will
// be played as fast as possible. If sync is TRUE, the timestamps of the incoming buffers will be used to
// schedule the exact render time of its contents.
func (g *GstBaseSink) SetSync(sync bool) { C.gst_base_sink_set_sync(g.Instance(), gboolean(sync)) }
// SetThrottleTime sets the time that will be inserted between rendered buffers. This can be used to control
// the maximum buffers per second that the sink will render.
func (g *GstBaseSink) SetThrottleTime(throttle uint64) {
C.gst_base_sink_set_throttle_time(g.Instance(), C.guint64(throttle))
}
// SetTsOffset adjusts the synchronization of sink with offset. A negative value will render buffers earlier
// than their timestamp. A positive value will delay rendering. This function can be used to fix playback of
// badly timestamped buffers.
func (g *GstBaseSink) SetTsOffset(offset time.Duration) {
C.gst_base_sink_set_ts_offset(g.Instance(), C.GstClockTimeDiff(offset.Nanoseconds()))
}
// Wait will wait for preroll to complete and will then block until timeout is reached. It is usually called by
// subclasses that use their own internal synchronization but want to let some synchronization (like EOS) be
// handled by the base class.
//
// This function should only be called with the PREROLL_LOCK held (like when receiving an EOS event in the
// ::event vmethod or when handling buffers in ::render).
//
// The timeout argument should be the running_time of when the timeout should happen and will be adjusted with any
// latency and offset configured in the sink.
func (g *GstBaseSink) Wait(timeout time.Duration) (ret gst.FlowReturn, jitter time.Duration) {
var jit C.GstClockTimeDiff
gret := C.gst_base_sink_wait(g.Instance(), C.GstClockTime(timeout.Nanoseconds()), &jit)
return gst.FlowReturn(gret), time.Duration(jit)
}
// WaitClock will block until timeout is reached. It is usually called by subclasses that use their own
// internal synchronization.
//
// If time is not valid, no synchronisation is done and GST_CLOCK_BADTIME is returned. Likewise, if synchronization
// is disabled in the element or there is no clock, no synchronization is done and GST_CLOCK_BADTIME is returned.
//
// This function should only be called with the PREROLL_LOCK held, like when receiving an EOS event in the event()
// vmethod or when receiving a buffer in the render() vmethod.
//
// The timeout argument should be the running_time of when this method should return and is not adjusted with any
// latency or offset configured in the sink.
func (g *GstBaseSink) WaitClock(timeout time.Duration) (ret gst.ClockReturn, jitter time.Duration) {
var jit C.GstClockTimeDiff
gret := C.gst_base_sink_wait_clock(g.Instance(), C.GstClockTime(timeout.Nanoseconds()), &jit)
return gst.ClockReturn(gret), time.Duration(jit)
}
// WaitPreroll will block until the preroll is complete.
//
// If the render() method performs its own synchronisation against the clock it must unblock when going from
// PLAYING to the PAUSED state and call this method before continuing to render the remaining data.
//
// If the render() method can block on something else than the clock, it must also be ready to unblock immediately
// on the unlock() method and cause the render() method to immediately call this function. In this case, the
// subclass must be prepared to continue rendering where it left off if this function returns GST_FLOW_OK.
//
// This function will block until a state change to PLAYING happens (in which case this function returns
// GST_FLOW_OK) or the processing must be stopped due to a state change to READY or a FLUSH event (in which case
// this function returns GST_FLOW_FLUSHING).
//
// This function should only be called with the PREROLL_LOCK held, like in the render function.
func (g *GstBaseSink) WaitPreroll() gst.FlowReturn {
return gst.FlowReturn(C.gst_base_sink_wait_preroll(g.Instance()))
}
+166
View File
@@ -0,0 +1,166 @@
package base
/*
#include "gst.go.h"
*/
import "C"
import (
"time"
"unsafe"
"github.com/tinyzimmer/go-gst/gst"
)
//export goGstBaseSinkActivatePull
func goGstBaseSinkActivatePull(sink *C.GstBaseSink, active C.gboolean) C.gboolean {
iface := gst.FromObjectUnsafePrivate(unsafe.Pointer(sink)).(interface {
ActivatePull(self *GstBaseSink, active bool) bool
})
return gboolean(iface.ActivatePull(wrapGstBaseSink(sink), gobool(active)))
}
//export goGstBaseSinkEvent
func goGstBaseSinkEvent(sink *C.GstBaseSink, event *C.GstEvent) C.gboolean {
iface := gst.FromObjectUnsafePrivate(unsafe.Pointer(sink)).(interface {
Event(self *GstBaseSink, event *gst.Event) bool
})
return gboolean(iface.Event(wrapGstBaseSink(sink), gst.FromGstEventUnsafe(unsafe.Pointer(event))))
}
//export goGstBaseSinkFixate
func goGstBaseSinkFixate(sink *C.GstBaseSink, caps *C.GstCaps) *C.GstCaps {
iface := gst.FromObjectUnsafePrivate(unsafe.Pointer(sink)).(interface {
Fixate(self *GstBaseSink, caps *gst.Caps) *gst.Caps
})
fixated := iface.Fixate(wrapGstBaseSink(sink), gst.FromGstCapsUnsafe(unsafe.Pointer(caps)))
if fixated == nil {
return nil
}
return (*C.GstCaps)(unsafe.Pointer(fixated.Instance()))
}
//export goGstBaseSinkGetCaps
func goGstBaseSinkGetCaps(sink *C.GstBaseSink, filter *C.GstCaps) *C.GstCaps {
iface := gst.FromObjectUnsafePrivate(unsafe.Pointer(sink)).(interface {
GetCaps(self *GstBaseSink, filter *gst.Caps) *gst.Caps
})
filtered := iface.GetCaps(wrapGstBaseSink(sink), gst.FromGstCapsUnsafe(unsafe.Pointer(filter)))
if filtered == nil {
return nil
}
return (*C.GstCaps)(unsafe.Pointer(filtered.Instance()))
}
//export goGstBaseSinkGetTimes
func goGstBaseSinkGetTimes(sink *C.GstBaseSink, buf *C.GstBuffer, start, end *C.GstClockTime) {
iface := gst.FromObjectUnsafePrivate(unsafe.Pointer(sink)).(interface {
GetTimes(self *GstBaseSink, buffer *gst.Buffer) (start, end time.Duration)
})
retStart, retEnd := iface.GetTimes(wrapGstBaseSink(sink), gst.FromGstBufferUnsafe(unsafe.Pointer(buf)))
*start = C.GstClockTime(retStart.Nanoseconds())
*end = C.GstClockTime(retEnd.Nanoseconds())
}
//export goGstBaseSinkPrepare
func goGstBaseSinkPrepare(sink *C.GstBaseSink, buf *C.GstBuffer) C.GstFlowReturn {
iface := gst.FromObjectUnsafePrivate(unsafe.Pointer(sink)).(interface {
Prepare(self *GstBaseSink, buffer *gst.Buffer) gst.FlowReturn
})
return C.GstFlowReturn(iface.Prepare(wrapGstBaseSink(sink), gst.FromGstBufferUnsafe(unsafe.Pointer(buf))))
}
//export goGstBaseSinkPrepareList
func goGstBaseSinkPrepareList(sink *C.GstBaseSink, list *C.GstBufferList) C.GstFlowReturn {
iface := gst.FromObjectUnsafePrivate(unsafe.Pointer(sink)).(interface {
PrepareList(self *GstBaseSink, bufferList *gst.BufferList) gst.FlowReturn
})
return C.GstFlowReturn(iface.PrepareList(wrapGstBaseSink(sink), gst.FromGstBufferListUnsafe(unsafe.Pointer(list))))
}
//export goGstBaseSinkPreroll
func goGstBaseSinkPreroll(sink *C.GstBaseSink, buf *C.GstBuffer) C.GstFlowReturn {
iface := gst.FromObjectUnsafePrivate(unsafe.Pointer(sink)).(interface {
Preroll(self *GstBaseSink, buffer *gst.Buffer) gst.FlowReturn
})
return C.GstFlowReturn(iface.Preroll(wrapGstBaseSink(sink), gst.FromGstBufferUnsafe(unsafe.Pointer(buf))))
}
//export goGstBaseSinkProposeAllocation
func goGstBaseSinkProposeAllocation(sink *C.GstBaseSink, query *C.GstQuery) C.gboolean {
iface := gst.FromObjectUnsafePrivate(unsafe.Pointer(sink)).(interface {
ProposeAllocation(self *GstBaseSink, query *gst.Query) bool
})
return gboolean(iface.ProposeAllocation(wrapGstBaseSink(sink), gst.FromGstQueryUnsafe(unsafe.Pointer(query))))
}
//export goGstBaseSinkQuery
func goGstBaseSinkQuery(sink *C.GstBaseSink, query *C.GstQuery) C.gboolean {
iface := gst.FromObjectUnsafePrivate(unsafe.Pointer(sink)).(interface {
Query(self *GstBaseSink, query *gst.Query) bool
})
return gboolean(iface.Query(wrapGstBaseSink(sink), gst.FromGstQueryUnsafe(unsafe.Pointer(query))))
}
//export goGstBaseSinkRender
func goGstBaseSinkRender(sink *C.GstBaseSink, buf *C.GstBuffer) C.GstFlowReturn {
iface := gst.FromObjectUnsafePrivate(unsafe.Pointer(sink)).(interface {
Render(self *GstBaseSink, buffer *gst.Buffer) gst.FlowReturn
})
return C.GstFlowReturn(iface.Render(wrapGstBaseSink(sink), gst.FromGstBufferUnsafe(unsafe.Pointer(buf))))
}
//export goGstBaseSinkRenderList
func goGstBaseSinkRenderList(sink *C.GstBaseSink, buf *C.GstBufferList) C.GstFlowReturn {
iface := gst.FromObjectUnsafePrivate(unsafe.Pointer(sink)).(interface {
RenderList(self *GstBaseSink, bufferList *gst.BufferList) gst.FlowReturn
})
return C.GstFlowReturn(iface.RenderList(wrapGstBaseSink(sink), gst.FromGstBufferListUnsafe(unsafe.Pointer(buf))))
}
//export goGstBaseSinkSetCaps
func goGstBaseSinkSetCaps(sink *C.GstBaseSink, caps *C.GstCaps) C.gboolean {
iface := gst.FromObjectUnsafePrivate(unsafe.Pointer(sink)).(interface {
SetCaps(self *GstBaseSink, caps *gst.Caps) bool
})
return gboolean(iface.SetCaps(wrapGstBaseSink(sink), gst.FromGstCapsUnsafe(unsafe.Pointer(caps))))
}
//export goGstBaseSinkStart
func goGstBaseSinkStart(sink *C.GstBaseSink) C.gboolean {
iface := gst.FromObjectUnsafePrivate(unsafe.Pointer(sink)).(interface {
Start(self *GstBaseSink) bool
})
return gboolean(iface.Start(wrapGstBaseSink(sink)))
}
//export goGstBaseSinkStop
func goGstBaseSinkStop(sink *C.GstBaseSink) C.gboolean {
iface := gst.FromObjectUnsafePrivate(unsafe.Pointer(sink)).(interface {
Stop(self *GstBaseSink) bool
})
return gboolean(iface.Stop(wrapGstBaseSink(sink)))
}
//export goGstBaseSinkUnlock
func goGstBaseSinkUnlock(sink *C.GstBaseSink) C.gboolean {
iface := gst.FromObjectUnsafePrivate(unsafe.Pointer(sink)).(interface {
Unlock(self *GstBaseSink) bool
})
return gboolean(iface.Unlock(wrapGstBaseSink(sink)))
}
//export goGstBaseSinkUnlockStop
func goGstBaseSinkUnlockStop(sink *C.GstBaseSink) C.gboolean {
iface := gst.FromObjectUnsafePrivate(unsafe.Pointer(sink)).(interface {
UnlockStop(self *GstBaseSink) bool
})
return gboolean(iface.UnlockStop(wrapGstBaseSink(sink)))
}
//export goGstBaseSinkWaitEvent
func goGstBaseSinkWaitEvent(sink *C.GstBaseSink, event *C.GstEvent) C.GstFlowReturn {
iface := gst.FromObjectUnsafePrivate(unsafe.Pointer(sink)).(interface {
WaitEvent(self *GstBaseSink, event *gst.Event) gst.FlowReturn
})
return C.GstFlowReturn(iface.WaitEvent(wrapGstBaseSink(sink), gst.FromGstEventUnsafe(unsafe.Pointer(event))))
}
+240
View File
@@ -0,0 +1,240 @@
package base
/*
#include "gst.go.h"
extern gboolean goGstBaseSinkActivatePull (GstBaseSink * sink, gboolean active);
extern gboolean goGstBaseSinkEvent (GstBaseSink * sink, GstEvent * event);
extern GstCaps * goGstBaseSinkFixate (GstBaseSink * sink, GstCaps * caps);
extern GstCaps * goGstBaseSinkGetCaps (GstBaseSink * sink, GstCaps * filter);
extern void goGstBaseSinkGetTimes (GstBaseSink * sink, GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
extern GstFlowReturn goGstBaseSinkPrepare (GstBaseSink * sink, GstBuffer * buffer);
extern GstFlowReturn goGstBaseSinkPrepareList (GstBaseSink * sink, GstBufferList * buffer_list);
extern GstFlowReturn goGstBaseSinkPreroll (GstBaseSink * sink, GstBuffer * buffer);
extern gboolean goGstBaseSinkProposeAllocation (GstBaseSink * sink, GstQuery * query);
extern gboolean goGstBaseSinkQuery (GstBaseSink * sink, GstQuery * query);
extern GstFlowReturn goGstBaseSinkRender (GstBaseSink * sink, GstBuffer * buffer);
extern GstFlowReturn goGstBaseSinkRenderList (GstBaseSink * sink, GstBufferList * buffer_list);
extern gboolean goGstBaseSinkSetCaps (GstBaseSink * sink, GstCaps * caps);
extern gboolean goGstBaseSinkStart (GstBaseSink * sink);
extern gboolean goGstBaseSinkStop (GstBaseSink * sink);
extern gboolean goGstBaseSinkUnlock (GstBaseSink * sink);
extern gboolean goGstBaseSinkUnlockStop (GstBaseSink * sink);
extern GstFlowReturn goGstBaseSinkWaitEvent (GstBaseSink * sink, GstEvent * event);
GstFlowReturn do_wait_event (GstBaseSink * sink, GstEvent * event)
{
GObjectClass * this_class = G_OBJECT_GET_CLASS(G_OBJECT(sink));
GstBaseSinkClass * parent = toGstBaseSinkClass(g_type_class_peek_parent(this_class));
GstFlowReturn ret = parent->wait_event(sink, event);
if (ret == GST_FLOW_ERROR)
return ret;
return goGstBaseSinkWaitEvent(sink, event);
}
void setGstBaseSinkActivatePull (GstBaseSinkClass * klass) { klass->activate_pull = goGstBaseSinkActivatePull; }
void setGstBaseSinkEvent (GstBaseSinkClass * klass) { klass->event = goGstBaseSinkEvent; }
void setGstBaseSinkFixate (GstBaseSinkClass * klass) { klass->fixate = goGstBaseSinkFixate; }
void setGstBaseSinkGetCaps (GstBaseSinkClass * klass) { klass->get_caps = goGstBaseSinkGetCaps; }
void setGstBaseSinkGetTimes (GstBaseSinkClass * klass) { klass->get_times = goGstBaseSinkGetTimes; }
void setGstBaseSinkPrepare (GstBaseSinkClass * klass) { klass->prepare = goGstBaseSinkPrepare; }
void setGstBaseSinkPrepareList (GstBaseSinkClass * klass) { klass->prepare_list = goGstBaseSinkPrepareList; }
void setGstBaseSinkPreroll (GstBaseSinkClass * klass) { klass->preroll = goGstBaseSinkPreroll; }
void setGstBaseSinkProposeAllocation (GstBaseSinkClass * klass) { klass->propose_allocation = goGstBaseSinkProposeAllocation; }
void setGstBaseSinkQuery (GstBaseSinkClass * klass) { klass->query = goGstBaseSinkQuery; }
void setGstBaseSinkRender (GstBaseSinkClass * klass) { klass->render = goGstBaseSinkRender; }
void setGstBaseSinkRenderList (GstBaseSinkClass * klass) { klass->render_list = goGstBaseSinkRenderList; }
void setGstBaseSinkSetCaps (GstBaseSinkClass * klass) { klass->set_caps = goGstBaseSinkSetCaps; }
void setGstBaseSinkStart (GstBaseSinkClass * klass) { klass->start = goGstBaseSinkStart; }
void setGstBaseSinkStop (GstBaseSinkClass * klass) { klass->stop = goGstBaseSinkStop; }
void setGstBaseSinkUnlock (GstBaseSinkClass * klass) { klass->unlock = goGstBaseSinkUnlock; }
void setGstBaseSinkUnlockStop (GstBaseSinkClass * klass) { klass->unlock_stop = goGstBaseSinkUnlockStop; }
void setGstBaseSinkWaitEvent (GstBaseSinkClass * klass) { klass->wait_event = do_wait_event; }
*/
import "C"
import (
"time"
"unsafe"
"github.com/tinyzimmer/go-glib/glib"
"github.com/tinyzimmer/go-gst/gst"
)
var (
// ExtendsBaseSink is an Extendable for extending a GstBaseSink
ExtendsBaseSink gst.Extendable = &extendsBaseSink{parent: gst.ExtendsElement}
)
// GstBaseSinkImpl is the documented interface for extending a GstBaseSink. 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 GstBaseSinkImpl interface {
// Subclasses should override this when they can provide an alternate method of spawning a thread to
// drive the pipeline in pull mode. Should start or stop the pulling thread, depending on the value
// of the "active" argument. Called after actually activating the sink pad in pull mode. The default
// implementation starts a task on the sink pad.
ActivatePull(self *GstBaseSink, active bool) bool
// Override this to handle events arriving on the sink pad
Event(self *GstBaseSink, event *gst.Event) bool
// Only useful in pull mode. Implement if you have ideas about what should be the default values for
// the caps you support.
Fixate(self *GstBaseSink, caps *gst.Caps) *gst.Caps
// Called to get sink pad caps from the subclass
GetCaps(self *GstBaseSink, filter *gst.Caps) *gst.Caps
// Called to get the start and end times for synchronising the passed buffer to the clock
GetTimes(self *GstBaseSink, buffer *gst.Buffer) (start, end time.Duration)
// Called to prepare the buffer for render and preroll. This function is called before synchronization
// is performed.
Prepare(self *GstBaseSink, buffer *gst.Buffer) gst.FlowReturn
// Called to prepare the buffer list for render_list. This function is called before synchronization is
// performed.
PrepareList(self *GstBaseSink, bufferList *gst.BufferList) gst.FlowReturn
// Called to present the preroll buffer if desired.
Preroll(self *GstBaseSink, buffer *gst.Buffer) gst.FlowReturn
// Used to configure the allocation query
ProposeAllocation(self *GstBaseSink, query *gst.Query) bool
// Handle queries on the element
Query(self *GstBaseSink, query *gst.Query) bool
// Called when a buffer should be presented or output, at the correct moment if the GstBaseSink has been
// set to sync to the clock.
Render(self *GstBaseSink, buffer *gst.Buffer) gst.FlowReturn
// Same as render but used with buffer lists instead of buffers.
RenderList(self *GstBaseSink, bufferList *gst.BufferList) gst.FlowReturn
// Notify subclass of changed caps
SetCaps(self *GstBaseSink, caps *gst.Caps) bool
// Start processing. Ideal for opening resources in the subclass
Start(self *GstBaseSink) bool
// Stop processing. Subclasses should use this to close resources.
Stop(self *GstBaseSink) bool
// Unlock any pending access to the resource. Subclasses should unblock any blocked function ASAP and call
// WaitPreroll
Unlock(self *GstBaseSink) bool
// Clear the previous unlock request. Subclasses should clear any state they set during Unlock(), and be ready
// to continue where they left off after WaitPreroll, Wait or WaitClock return or Render() is called again.
UnlockStop(self *GstBaseSink) bool
// Override this to implement custom logic to wait for the event time (for events like EOS and GAP). The bindings
// take care of first chaining up to the parent class.
WaitEvent(self *GstBaseSink, event *gst.Event) gst.FlowReturn
}
type extendsBaseSink struct{ parent gst.Extendable }
func (e *extendsBaseSink) Type() glib.Type { return glib.Type(C.gst_base_sink_get_type()) }
func (e *extendsBaseSink) ClassSize() int64 { return int64(C.sizeof_GstBaseSinkClass) }
func (e *extendsBaseSink) InstanceSize() int64 { return int64(C.sizeof_GstBaseSink) }
func (e *extendsBaseSink) InitClass(klass unsafe.Pointer, elem gst.GoElement) {
e.parent.InitClass(klass, elem)
sinkClass := C.toGstBaseSinkClass(klass)
if _, ok := elem.(interface {
ActivatePull(self *GstBaseSink, active bool) bool
}); ok {
C.setGstBaseSinkActivatePull(sinkClass)
}
if _, ok := elem.(interface {
Event(self *GstBaseSink, event *gst.Event) bool
}); ok {
C.setGstBaseSinkEvent(sinkClass)
}
if _, ok := elem.(interface {
Fixate(self *GstBaseSink, caps *gst.Caps) *gst.Caps
}); ok {
C.setGstBaseSinkFixate(sinkClass)
}
if _, ok := elem.(interface {
GetCaps(self *GstBaseSink, filter *gst.Caps) *gst.Caps
}); ok {
C.setGstBaseSinkGetCaps(sinkClass)
}
if _, ok := elem.(interface {
GetTimes(self *GstBaseSink, buffer *gst.Buffer) (start, end time.Duration)
}); ok {
C.setGstBaseSinkGetTimes(sinkClass)
}
if _, ok := elem.(interface {
Prepare(self *GstBaseSink, buffer *gst.Buffer) gst.FlowReturn
}); ok {
C.setGstBaseSinkPrepare(sinkClass)
}
if _, ok := elem.(interface {
PrepareList(self *GstBaseSink, bufferList *gst.BufferList) gst.FlowReturn
}); ok {
C.setGstBaseSinkPrepareList(sinkClass)
}
if _, ok := elem.(interface {
Preroll(self *GstBaseSink, buffer *gst.Buffer) gst.FlowReturn
}); ok {
C.setGstBaseSinkPreroll(sinkClass)
}
if _, ok := elem.(interface {
ProposeAllocation(self *GstBaseSink, query *gst.Query) bool
}); ok {
C.setGstBaseSinkProposeAllocation(sinkClass)
}
if _, ok := elem.(interface {
Query(self *GstBaseSink, query *gst.Query) bool
}); ok {
C.setGstBaseSinkQuery(sinkClass)
}
if _, ok := elem.(interface {
Render(self *GstBaseSink, buffer *gst.Buffer) gst.FlowReturn
}); ok {
C.setGstBaseSinkRender(sinkClass)
}
if _, ok := elem.(interface {
RenderList(self *GstBaseSink, bufferList *gst.BufferList) gst.FlowReturn
}); ok {
C.setGstBaseSinkRenderList(sinkClass)
}
if _, ok := elem.(interface {
SetCaps(self *GstBaseSink, caps *gst.Caps) bool
}); ok {
C.setGstBaseSinkSetCaps(sinkClass)
}
if _, ok := elem.(interface {
Start(self *GstBaseSink) bool
}); ok {
C.setGstBaseSinkStart(sinkClass)
}
if _, ok := elem.(interface {
Stop(self *GstBaseSink) bool
}); ok {
C.setGstBaseSinkStop(sinkClass)
}
if _, ok := elem.(interface {
Unlock(self *GstBaseSink) bool
}); ok {
C.setGstBaseSinkUnlock(sinkClass)
}
if _, ok := elem.(interface {
UnlockStop(self *GstBaseSink) bool
}); ok {
C.setGstBaseSinkUnlockStop(sinkClass)
}
if _, ok := elem.(interface {
WaitEvent(self *GstBaseSink, event *gst.Event) gst.FlowReturn
}); ok {
C.setGstBaseSinkWaitEvent(sinkClass)
}
}
+142
View File
@@ -5,6 +5,7 @@ package base
*/
import "C"
import (
"time"
"unsafe"
"github.com/tinyzimmer/go-gst/gst"
@@ -28,6 +29,106 @@ func (g *GstBaseSrc) Instance() *C.GstBaseSrc {
return C.toGstBaseSrc(g.Unsafe())
}
// GetAllocator retrieves the memory allocator used by this base src. Unref after usage.
func (g *GstBaseSrc) GetAllocator() (*gst.Allocator, *gst.AllocationParams) {
var allocParams C.GstAllocationParams
var allocator *C.GstAllocator
C.gst_base_src_get_allocator(g.Instance(), &allocator, &allocParams)
return gst.FromGstAllocatorUnsafe(unsafe.Pointer(allocator)), gst.FromGstAllocationParamsUnsafe(unsafe.Pointer(&allocParams))
}
// GetBlocksize returns the number of bytes that the source will push out with each buffer.
func (g *GstBaseSrc) GetBlocksize() uint { return uint(C.gst_base_src_get_blocksize(g.Instance())) }
// GetBufferPool returns the BufferPool used by this source. Unref after usage.
func (g *GstBaseSrc) GetBufferPool() *gst.BufferPool {
return gst.FromGstBufferPoolUnsafe(unsafe.Pointer(C.gst_base_src_get_buffer_pool(g.Instance())))
}
// DoTimestamp will query if the timestamps outgoing on this source's buffers are based on the current
// running time.
func (g *GstBaseSrc) DoTimestamp() bool {
return gobool(C.gst_base_src_get_do_timestamp(g.Instance()))
}
// IsAsync retrieves the current async behavior of the source.
func (g *GstBaseSrc) IsAsync() bool { return gobool(C.gst_base_src_is_async(g.Instance())) }
// IsLive checks if this source is in live mode.
func (g *GstBaseSrc) IsLive() bool { return gobool(C.gst_base_src_is_live(g.Instance())) }
// Negotiate negotiates this source's pad caps with downstream elements. Do not call this in the Fill()
// vmethod. Call this in Create() or in Alloc(), before any buffer is allocated.
func (g *GstBaseSrc) Negotiate() bool { return gobool(C.gst_base_src_negotiate(g.Instance())) }
// NewSegment prepares a new segment for emission downstream. This function must only be called by derived
// sub-classes, and only from the create function, as the stream-lock needs to be held.
//
// The format for the segment must be identical with the current format of the source, as configured with
// SetFormat.
//
// The format of src must not be gst.FormatUndefined and the format should be configured via SetFormat before
// calling this method.
func (g *GstBaseSrc) NewSegment(segment *gst.Segment) bool {
return gobool(C.gst_base_src_new_segment(g.Instance(), (*C.GstSegment)(unsafe.Pointer(segment.Instance()))))
}
// QueryLatency queries the source for the latency parameters. live will be TRUE when src is configured as a
// live source. minLatency and maxLatency will be set to the difference between the running time and the timestamp
// of the first buffer.
//
// This function is mostly used by subclasses.
func (g *GstBaseSrc) QueryLatency() (ok, live bool, minLatency, maxLatency time.Duration) {
var glive C.gboolean
var gmin C.GstClockTime
var gmax C.GstClockTime
gok := C.gst_base_src_query_latency(g.Instance(), &glive, &gmin, &gmax)
return gobool(gok), gobool(glive), time.Duration(gmin), time.Duration(gmax)
}
// SetAsync configures async behaviour in src, no state change will block. The open, close, start, stop, play and
// pause virtual methods will be executed in a different thread and are thus allowed to perform blocking operations.
// Any blocking operation should be unblocked with the unlock vmethod.
func (g *GstBaseSrc) SetAsync(async bool) { C.gst_base_src_set_async(g.Instance(), gboolean(async)) }
// SetAutomaticEOS sets whether EOS should be automatically emmitted.
//
// If automaticEOS is TRUE, src will automatically go EOS if a buffer after the total size is returned. By default
// this is TRUE but sources that can't return an authoritative size and only know that they're EOS when trying to
// read more should set this to FALSE.
//
// When src operates in gst.FormatTime, GstBaseSrc will send an EOS when a buffer outside of the currently configured
// segment is pushed if automaticEOS is TRUE. Since 1.16, if automatic_eos is FALSE an EOS will be pushed only when
// the Create() implementation returns gst.FlowEOS.
func (g *GstBaseSrc) SetAutomaticEOS(automaticEOS bool) {
C.gst_base_src_set_automatic_eos(g.Instance(), gboolean(automaticEOS))
}
// SetBlocksize sets the number of bytes that src will push out with each buffer. When blocksize is set to -1, a
// default length will be used.
func (g *GstBaseSrc) SetBlocksize(size uint) {
C.gst_base_src_set_blocksize(g.Instance(), C.guint(size))
}
// SetCaps sets new caps on the source pad.
func (g *GstBaseSrc) SetCaps(caps *gst.Caps) bool {
return gobool(C.gst_base_src_set_caps(g.Instance(), (*C.GstCaps)(unsafe.Pointer(caps.Instance()))))
}
// SetDoTimestamp configures src to automatically timestamp outgoing buffers based on the current running_time of the pipeline.
// This property is mostly useful for live sources.
func (g *GstBaseSrc) SetDoTimestamp(doTimestamp bool) {
C.gst_base_src_set_do_timestamp(g.Instance(), gboolean(doTimestamp))
}
// SetDynamicSize sets if the size is dynamic for this source.
//
// If not dynamic, size is only updated when needed, such as when trying to read past current tracked size. Otherwise, size is
// checked for upon each read.
func (g *GstBaseSrc) SetDynamicSize(dynamic bool) {
C.gst_base_src_set_dynamic_size(g.Instance(), gboolean(dynamic))
}
// SetFormat sets the default format of the source. This will be the format used for sending
// SEGMENT events and for performing seeks.
//
@@ -39,9 +140,50 @@ func (g *GstBaseSrc) SetFormat(format gst.Format) {
C.gst_base_src_set_format(g.Instance(), C.GstFormat(format))
}
// SetLive sets if the element listens to a live source.
//
// A live source will not produce data in the PAUSED state and will therefore not be able to participate in the
// PREROLL phase of a pipeline. To signal this fact to the application and the pipeline, the state change return
// value of the live source will be gst.StateChangeNoPreroll.
func (g *GstBaseSrc) SetLive(live bool) { C.gst_base_src_set_live(g.Instance(), gboolean(live)) }
// 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))
}
// StartWait waits until the start operation is complete.
func (g *GstBaseSrc) StartWait() gst.FlowReturn {
return gst.FlowReturn(C.gst_base_src_start_wait(g.Instance()))
}
// SubmitBufferList submits a list of buffers to the source.
//
// Subclasses can call this from their create virtual method implementation to submit a buffer list to be pushed out
// later. This is useful in cases where the create function wants to produce multiple buffers to be pushed out in one
// go in form of a GstBufferList, which can reduce overhead drastically, especially for packetised inputs (for data
// streams where the packetisation/chunking is not important it is usually more efficient to return larger buffers instead).
//
// Subclasses that use this function from their create function must return GST_FLOW_OK and no buffer from their create
// virtual method implementation. If a buffer is returned after a buffer list has also been submitted via this function the
// behaviour is undefined.
//
// Subclasses must only call this function once per create function call and subclasses must only call this function when
// the source operates in push mode.
func (g *GstBaseSrc) SubmitBufferList(bufferList *gst.BufferList) {
C.gst_base_src_submit_buffer_list(g.Instance(), (*C.GstBufferList)(unsafe.Pointer(bufferList.Instance())))
}
// WaitPlaying will block until a state change to PLAYING happens (in which case this function returns gst.FlowOK) or the
// processing must be stopped due to a state change to READY or a FLUSH event (in which case this function returns GST_FLOW_FLUSHING).
//
// If the Create() method performs its own synchronisation against the clock it must unblock when going from PLAYING to the PAUSED
// state and call this method before continuing to produce the remaining data.
//
// gst.FlowOK will be returned if the source is PLAYING and processing can continue. Any other return value should be
// returned from the Create() vmethod.
func (g *GstBaseSrc) WaitPlaying() gst.FlowReturn {
return gst.FlowReturn(C.gst_base_src_wait_playing(g.Instance()))
}
+5
View File
@@ -10,3 +10,8 @@ func gboolean(b bool) C.gboolean {
}
return C.gboolean(0)
}
// gobool provides an easy type conversion between C.gboolean and a go bool.
func gobool(b C.gboolean) bool {
return int(b) > 0
}
+10
View File
@@ -14,6 +14,11 @@ type AllocationParams struct {
ptr *C.GstAllocationParams
}
// FromGstAllocationParamsUnsafe wraps the given unsafe.Pointer in an AllocationParams instance.
func FromGstAllocationParamsUnsafe(alloc unsafe.Pointer) *AllocationParams {
return &AllocationParams{ptr: (*C.GstAllocationParams)(alloc)}
}
// NewAllocationParams initializes a set of allocation params with the default
// values.
func NewAllocationParams() *AllocationParams {
@@ -65,6 +70,11 @@ func (a *AllocationParams) SetPadding(padding int64) { a.ptr.padding = C.gsize(p
// Allocator is a go representation of a GstAllocator
type Allocator struct{ *Object }
// FromGstAllocatorUnsafe wraps the given unsafe.Pointer in an Allocator instance.
func FromGstAllocatorUnsafe(alloc unsafe.Pointer) *Allocator {
return wrapAllocator(toGObject(alloc))
}
// DefaultAllocator returns the default GstAllocator.
func DefaultAllocator() *Allocator {
return wrapAllocator(&glib.Object{GObject: glib.ToGObject(unsafe.Pointer(C.gst_allocator_find(nil)))})
+17 -3
View File
@@ -22,9 +22,17 @@ type BufferList struct {
ptr *C.GstBufferList
}
// NewBufferList returns a new empty BufferList.
func NewBufferList() *BufferList {
return wrapBufferList(C.gst_buffer_list_new())
// NewBufferList returns a new BufferList. The given slice can be nil and the returned
// buffer list will be empty.
func NewBufferList(buffers []*Buffer) *BufferList {
bufList := wrapBufferList(C.gst_buffer_list_new())
if buffers == nil {
return bufList
}
for idx, buf := range buffers {
bufList.Insert(idx, buf)
}
return bufList
}
// NewBufferListSized creates a new BufferList with the given size.
@@ -32,6 +40,12 @@ func NewBufferListSized(size uint) *BufferList {
return wrapBufferList(C.gst_buffer_list_new_sized(C.guint(size)))
}
// FromGstBufferListUnsafe wraps the given unsafe.Pointer in a BufferList instance. It is meant for internal usage
// and exported for visibility to other packages.
func FromGstBufferListUnsafe(ptr unsafe.Pointer) *BufferList {
return wrapBufferList((*C.GstBufferList)(ptr))
}
// Instance returns the underlying GstBufferList.
func (b *BufferList) Instance() *C.GstBufferList { return C.toGstBufferList(unsafe.Pointer(b.ptr)) }
+5
View File
@@ -22,6 +22,11 @@ func NewBufferPool() *BufferPool {
return wrapBufferPool(&glib.Object{GObject: glib.ToGObject(unsafe.Pointer(pool))})
}
// FromGstBufferPoolUnsafe wraps the given unsafe.Pointer in a BufferPool instance.
func FromGstBufferPoolUnsafe(bufferPool unsafe.Pointer) *BufferPool {
return wrapBufferPool(toGObject(bufferPool))
}
// Instance returns the underlying GstBufferPool instance.
func (b *BufferPool) Instance() *C.GstBufferPool { return C.toGstBufferPool(b.Unsafe()) }
+1 -2
View File
@@ -21,7 +21,6 @@ import "C"
import (
"errors"
"fmt"
"reflect"
"runtime/debug"
"sync"
@@ -327,7 +326,7 @@ func (b *Bus) PostError(src interface{}, msg string, err error) bool {
if err != nil {
st = NewStructure("go-error")
if addErr := st.SetValue("error", err.Error()); addErr != nil {
fmt.Println("go-gst-warning: failed to set error message to structure")
b.Log(CAT, LevelWarning, "failed to set error message to structure")
}
}
gstMsg := NewErrorMessage(src, gerr, string(debug.Stack()), st)
+47 -2
View File
@@ -17,10 +17,55 @@ func FromGstSampleUnsafe(sample unsafe.Pointer) *Sample { return wrapSample(C.to
// Instance returns the underlying *GstSample instance.
func (s *Sample) Instance() *C.GstSample { return C.toGstSample(unsafe.Pointer(s.sample)) }
// Unref calls gst_sample_unref on the sample.
func (s *Sample) Unref() { C.gst_sample_unref((*C.GstSample)(s.Instance())) }
// Ref increases the ref count on the sample.
func (s *Sample) Ref() *Sample {
return wrapSample(C.gst_sample_ref(s.Instance()))
}
// Copy creates a copy of the given sample. This will also make a newly allocated copy of the data
// the source sample contains.
func (s *Sample) Copy() *Sample { return wrapSample(C.gst_sample_copy(s.Instance())) }
// GetBuffer returns the buffer inside this sample.
func (s *Sample) GetBuffer() *Buffer {
return wrapBuffer(C.gst_sample_get_buffer((*C.GstSample)(s.Instance())))
}
// GetBufferList gets the buffer list associated with this sample.
func (s *Sample) GetBufferList() *BufferList {
return wrapBufferList(C.gst_sample_get_buffer_list(s.Instance()))
}
// GetCaps returns the caps associated with this sample. Take a ref if you need to hold on to them
// longer then the life of the sample.
func (s *Sample) GetCaps() *Caps { return wrapCaps(C.gst_sample_get_caps(s.Instance())) }
// GetInfo gets extra information about this sample. The structure remains valid as long as sample is valid.
func (s *Sample) GetInfo() *Structure { return wrapStructure(C.gst_sample_get_info(s.Instance())) }
// GetSegment gets the segment associated with the sample. The segmenr remains valid as long as sample is valid.
func (s *Sample) GetSegment() *Segment { return wrapSegment(C.gst_sample_get_segment(s.Instance())) }
// SetBuffer sets the buffer inside this sample. The sample must be writable.
func (s *Sample) SetBuffer(buf *Buffer) { C.gst_sample_set_buffer(s.Instance(), buf.Instance()) }
// SetBufferList sets the buffer list for this sample. The sample must be writable.
func (s *Sample) SetBufferList(buf *BufferList) {
C.gst_sample_set_buffer_list(s.Instance(), buf.Instance())
}
// SetCaps sets the caps on this sample. The sample must be writable.
func (s *Sample) SetCaps(caps *Caps) { C.gst_sample_set_caps(s.Instance(), caps.Instance()) }
// SetInfo sets the info on this sample. The sample must be writable.
func (s *Sample) SetInfo(st *Structure) bool {
return gobool(C.gst_sample_set_info(s.Instance(), st.Instance()))
}
// SetSegment sets the segment on this sample. The sample must be writable.
func (s *Sample) SetSegment(segment *Segment) {
C.gst_sample_set_segment(s.Instance(), segment.Instance())
}
// Unref calls gst_sample_unref on the sample.
func (s *Sample) Unref() { C.gst_sample_unref((*C.GstSample)(s.Instance())) }
+6
View File
@@ -75,6 +75,12 @@ func MarshalStructure(data interface{}) *Structure {
return st
}
// FromGstStructureUnsafe wraps the given unsafe.Pointer in a Structure. This is meant for internal usage
// and is exported for visibility to other packages.
func FromGstStructureUnsafe(st unsafe.Pointer) *Structure {
return wrapStructure((*C.GstStructure)(st))
}
// UnmarshalInto will unmarshal this structure into the given pointer. The object
// reflected by the pointer must be non-nil.
func (s *Structure) UnmarshalInto(data interface{}) error {
+2 -2
View File
@@ -1,9 +1,9 @@
package gstauto
import "fmt"
import "github.com/tinyzimmer/go-gst/gst"
func runOrPrintErr(f func() error) {
if err := f(); err != nil {
fmt.Println("[go-gst/gst/gstauto] Internal Error:", err.Error())
gst.CAT.Log(gst.LevelError, err.Error())
}
}