diff --git a/README.md b/README.md index b75f307..4cd6a47 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,9 @@ For building applications with this library you need the following: - `gcc` and `pkg-config` - `libgstreamer-1.0-dev`: This package name may be different depending on your OS. You need the `gst.h` header files. - In some distributions (such as alpine linux) this is in the `gstreamer-dev` package. - - `libgstreamer-app-1.0-dev`: This package name may also be different depending on your os. You need the `gstappsink.h` and `gstappsrc.h` - - In some distributions (such as alpine linux) this is in the `gst-plugins-base-dev` package. - - I will likely turn this into an optional dependency. + - To use the `app` or `gstauto/app` packages you will need additional dependencies: + - `libgstreamer-app-1.0-dev`: This package name may also be different depending on your os. You need the `gstappsink.h` and `gstappsrc.h` + - In some distributions (such as alpine linux) this is in the `gst-plugins-base-dev` package. - You may need platform specific headers also. For example, in alpine linux, you will most likely also need the `musl-dev` package. For running applications with this library you'll need to have `libgstreamer-1.0` installed. Again, this package may be different depending on your OS. diff --git a/cmd/go-gst/gif.go b/cmd/go-gst/gif.go index 33d17a5..7ee8721 100644 --- a/cmd/go-gst/gif.go +++ b/cmd/go-gst/gif.go @@ -13,7 +13,7 @@ import ( "github.com/spf13/cobra" "github.com/tinyzimmer/go-gst/gst" - "github.com/tinyzimmer/go-gst/gst/gstauto" + "github.com/tinyzimmer/go-gst/gst/gstauto/app" ) var framesPerSecond int @@ -74,7 +74,7 @@ func gifEncode(cmd *cobra.Command, args []string) error { logInfo("gif", "Converting video to image frames") - gstPipeline, err := gstauto.NewPipelineReaderAppFromString(launchStr) + gstPipeline, err := app.NewPipelineReaderAppFromString(launchStr) if err != nil { return err } diff --git a/gst/app/doc.go b/gst/app/doc.go new file mode 100644 index 0000000..b3bccb3 --- /dev/null +++ b/gst/app/doc.go @@ -0,0 +1,10 @@ +/* +Package app contains bindings for the gstreamer-app C API. If you are trying +to build simple pipelines quickly (and optiionally readers/writers) see +the gstauto/app package. + +The location of this library may be different depending on your OS. It is usually +either with the gst-plugins-base development headers or a separate package called +gstreamer-app. +*/ +package app diff --git a/gst/app/gst.go.c b/gst/app/gst.go.c new file mode 100644 index 0000000..499c09d --- /dev/null +++ b/gst/app/gst.go.c @@ -0,0 +1,14 @@ +#include +#include + +GstAppSink * +toGstAppSink(void *p) +{ + return (GST_APP_SINK(p)); +} + +GstAppSrc * +toGstAppSrc(void *p) +{ + return (GST_APP_SRC(p)); +} diff --git a/gst/app/gst.go.h b/gst/app/gst.go.h new file mode 100644 index 0000000..146755e --- /dev/null +++ b/gst/app/gst.go.h @@ -0,0 +1,5 @@ +#include +#include + +GstAppSink * toGstAppSink (void *p); +GstAppSrc * toGstAppSrc (void *p); diff --git a/gst/gst_app_sink.go b/gst/app/gst_app_sink.go similarity index 62% rename from gst/gst_app_sink.go rename to gst/app/gst_app_sink.go index 3280b82..27dd02c 100644 --- a/gst/gst_app_sink.go +++ b/gst/app/gst_app_sink.go @@ -1,15 +1,20 @@ -package gst +package app // #include "gst.go.h" import "C" -import "errors" +import ( + "errors" + "unsafe" -// AppSink wraps an Element object with additional methods for pulling samples. -type AppSink struct{ *Element } + "github.com/tinyzimmer/go-gst/gst" +) + +// Sink wraps an Element made with the appsink plugin with additional methods for pulling samples. +type Sink struct{ *gst.Element } // NewAppSink returns a new appsink element. Unref after usage. -func NewAppSink() (*AppSink, error) { - elem, err := NewElement("appsink") +func NewAppSink() (*Sink, error) { + elem, err := gst.NewElement("appsink") if err != nil { return nil, err } @@ -17,19 +22,19 @@ func NewAppSink() (*AppSink, error) { } // Instance returns the native GstAppSink instance. -func (a *AppSink) Instance() *C.GstAppSink { return C.toGstAppSink(a.unsafe()) } +func (a *Sink) Instance() *C.GstAppSink { return C.toGstAppSink(a.Unsafe()) } // ErrEOS represents that the stream has ended. var ErrEOS = errors.New("Pipeline has reached end-of-stream") // IsEOS returns true if this AppSink has reached the end-of-stream. -func (a *AppSink) IsEOS() bool { +func (a *Sink) IsEOS() bool { return gobool(C.gst_app_sink_is_eos((*C.GstAppSink)(a.Instance()))) } // BlockPullSample will block until a sample becomes available or the stream // is ended. -func (a *AppSink) BlockPullSample() (*Sample, error) { +func (a *Sink) BlockPullSample() (*gst.Sample, error) { for { if a.IsEOS() { return nil, ErrEOS @@ -39,12 +44,12 @@ func (a *AppSink) BlockPullSample() (*Sample, error) { if sample == nil { continue } - return wrapSample(sample), nil + return gst.FromGstSampleUnsafe(unsafe.Pointer(sample)), nil } } // PullSample will try to pull a sample or return nil if none is available. -func (a *AppSink) PullSample() (*Sample, error) { +func (a *Sink) PullSample() (*gst.Sample, error) { if a.IsEOS() { return nil, ErrEOS } @@ -53,7 +58,7 @@ func (a *AppSink) PullSample() (*Sample, error) { C.GST_SECOND, ) if sample != nil { - return wrapSample(sample), nil + return gst.FromGstSampleUnsafe(unsafe.Pointer(sample)), nil } return nil, nil } diff --git a/gst/gst_app_src.go b/gst/app/gst_app_src.go similarity index 54% rename from gst/gst_app_src.go rename to gst/app/gst_app_src.go index 2f9c2cf..9ef3a2e 100644 --- a/gst/gst_app_src.go +++ b/gst/app/gst_app_src.go @@ -1,15 +1,20 @@ -package gst +package app // #include "gst.go.h" import "C" -import "time" +import ( + "time" + "unsafe" -// AppSrc wraps an Element object with additional methods for pushing samples. -type AppSrc struct{ *Element } + "github.com/tinyzimmer/go-gst/gst" +) + +// Source wraps an Element made with the appsrc plugin with additional methods for pushing samples. +type Source struct{ *gst.Element } // NewAppSrc returns a new AppSrc element. -func NewAppSrc() (*AppSrc, error) { - elem, err := NewElement("appsrc") +func NewAppSrc() (*Source, error) { + elem, err := gst.NewElement("appsrc") if err != nil { return nil, err } @@ -17,33 +22,36 @@ func NewAppSrc() (*AppSrc, error) { } // Instance returns the native GstAppSink instance. -func (a *AppSrc) Instance() *C.GstAppSrc { return C.toGstAppSrc(a.unsafe()) } +func (a *Source) Instance() *C.GstAppSrc { return C.toGstAppSrc(a.Unsafe()) } // SetSize sets the size of the source stream in bytes. You should call this for // streams of fixed length. -func (a *AppSrc) SetSize(size int64) { +func (a *Source) SetSize(size int64) { C.gst_app_src_set_size((*C.GstAppSrc)(a.Instance()), (C.gint64)(size)) } // SetDuration sets the duration of the source stream. You should call // this if the value is known. -func (a *AppSrc) SetDuration(dur time.Duration) { +func (a *Source) SetDuration(dur time.Duration) { C.gst_app_src_set_duration((*C.GstAppSrc)(a.Instance()), (C.ulong)(dur.Nanoseconds())) } // EndStream signals to the app source that the stream has ended after the last queued // buffer. -func (a *AppSrc) EndStream() FlowReturn { +func (a *Source) EndStream() gst.FlowReturn { ret := C.gst_app_src_end_of_stream((*C.GstAppSrc)(a.Instance())) - return FlowReturn(ret) + return gst.FlowReturn(ret) } // SetLive sets whether or not this is a live stream. -func (a *AppSrc) SetLive(b bool) error { return a.Set("is-live", b) } +func (a *Source) SetLive(b bool) error { return a.Set("is-live", b) } // PushBuffer pushes a buffer to the appsrc. Currently by default this will block // until the source is ready to accept the buffer. -func (a *AppSrc) PushBuffer(buf *Buffer) FlowReturn { - ret := C.gst_app_src_push_buffer((*C.GstAppSrc)(a.Instance()), (*C.GstBuffer)(buf.Instance())) - return FlowReturn(ret) +func (a *Source) PushBuffer(buf *gst.Buffer) gst.FlowReturn { + ret := C.gst_app_src_push_buffer( + (*C.GstAppSrc)(a.Instance()), + (*C.GstBuffer)(unsafe.Pointer(buf.Instance())), + ) + return gst.FlowReturn(ret) } diff --git a/gst/app/pkg_config.go b/gst/app/pkg_config.go new file mode 100644 index 0000000..8196118 --- /dev/null +++ b/gst/app/pkg_config.go @@ -0,0 +1,7 @@ +package app + +/* +#cgo pkg-config: gstreamer-1.0 gstreamer-app-1.0 +#cgo CFLAGS: -Wno-deprecated-declarations -g -Wall +*/ +import "C" diff --git a/gst/app/wrappers.go b/gst/app/wrappers.go new file mode 100644 index 0000000..b32eb3d --- /dev/null +++ b/gst/app/wrappers.go @@ -0,0 +1,22 @@ +package app + +// #include +import "C" + +import "github.com/tinyzimmer/go-gst/gst" + +func wrapAppSink(elem *gst.Element) *Sink { return &Sink{elem} } +func wrapAppSrc(elem *gst.Element) *Source { return &Source{elem} } + +// gobool provides an easy type conversion between C.gboolean and a go bool. +func gobool(b C.gboolean) bool { + return b != 0 +} + +// 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) +} diff --git a/gst/c_util.go b/gst/c_util.go index 066684c..f1dfed5 100644 --- a/gst/c_util.go +++ b/gst/c_util.go @@ -1,45 +1,11 @@ package gst -/* -#cgo pkg-config: gstreamer-1.0 gstreamer-app-1.0 -#cgo CFLAGS: -Wno-deprecated-declarations -g -Wall -#include -*/ +// #include import "C" import ( - "errors" "unsafe" - - "github.com/gotk3/gotk3/glib" ) -// Init is a wrapper around gst_init() and must be called before any -// other gstreamer calls and is used to initialize everything necessary. -// In addition to setting up gstreamer for usage, a pointer to a slice of -// strings may be passed in to parse standard gst command line arguments. -// args will be modified to remove any flags that were handled. -// Alternatively, nil may be passed in to not perform any command line -// parsing. -func Init(args *[]string) { - if args != nil { - argc := C.int(len(*args)) - argv := make([]*C.char, argc) - for i, arg := range *args { - argv[i] = C.CString(arg) - } - C.gst_init((*C.int)(unsafe.Pointer(&argc)), - (***C.char)(unsafe.Pointer(&argv))) - unhandled := make([]string, argc) - for i := 0; i < int(argc); i++ { - unhandled[i] = C.GoString(argv[i]) - C.free(unsafe.Pointer(argv[i])) - } - *args = unhandled - } else { - C.gst_init(nil, nil) - } -} - // gobool provides an easy type conversion between C.gboolean and a go bool. func gobool(b C.gboolean) bool { return b != 0 @@ -53,28 +19,7 @@ func gboolean(b bool) C.gboolean { return C.gboolean(0) } -func iteratorToElementSlice(iterator *C.GstIterator) ([]*Element, error) { - elems := make([]*Element, 0) - gval := new(C.GValue) - - for { - switch C.gst_iterator_next((*C.GstIterator)(iterator), (*C.GValue)(unsafe.Pointer(gval))) { - case C.GST_ITERATOR_DONE: - C.gst_iterator_free((*C.GstIterator)(iterator)) - return elems, nil - case C.GST_ITERATOR_RESYNC: - C.gst_iterator_resync((*C.GstIterator)(iterator)) - case C.GST_ITERATOR_OK: - cElemVoid := C.g_value_get_object((*C.GValue)(gval)) - cElem := (*C.GstElement)(cElemVoid) - elems = append(elems, wrapElement(glib.Take(unsafe.Pointer(cElem)))) - C.g_value_reset((*C.GValue)(gval)) - default: - return nil, errors.New("Element iterator failed") - } - } -} - +// goStrings returns a string slice for an array of size argc starting at the address argv. func goStrings(argc C.int, argv **C.gchar) []string { length := int(argc) tmpslice := (*[1 << 30]*C.gchar)(unsafe.Pointer(argv))[:length:length] @@ -85,6 +30,8 @@ func goStrings(argc C.int, argv **C.gchar) []string { return gostrings } +// newQuarkFromString creates a new GQuark (or returns an existing one) for the given +// string func newQuarkFromString(str string) C.uint { cstr := C.CString(str) defer C.free(unsafe.Pointer(cstr)) diff --git a/gst/gst.go.c b/gst/gst.go.c index fb2c4c3..76aa7a5 100644 --- a/gst/gst.go.c +++ b/gst/gst.go.c @@ -1,6 +1,4 @@ #include -#include -#include #include "gst.go.h" /* @@ -183,18 +181,6 @@ toGstElement(void *p) return (GST_ELEMENT(p)); } -GstAppSink * -toGstAppSink(void *p) -{ - return (GST_APP_SINK(p)); -} - -GstAppSrc * -toGstAppSrc(void *p) -{ - return (GST_APP_SRC(p)); -} - GstBin * toGstBin(void *p) { @@ -272,3 +258,9 @@ toGstBufferPool(void *p) { return (GST_BUFFER_POOL(p)); } + +GstSample * +toGstSample(void *p) +{ + return (GST_SAMPLE(p)); +} diff --git a/gst/gst.go.h b/gst/gst.go.h index a806a12..aa39d5e 100644 --- a/gst/gst.go.h +++ b/gst/gst.go.h @@ -1,6 +1,4 @@ #include -#include -#include extern gboolean structForEachCb (GQuark field_id, GValue * value, gpointer user_data); @@ -48,8 +46,6 @@ GstPluginFeature * toGstPluginFeature (void *p); GstObject * toGstObject (void *p); GstElementFactory * toGstElementFactory (void *p); GstElement * toGstElement (void *p); -GstAppSink * toGstAppSink (void *p); -GstAppSrc * toGstAppSrc (void *p); GstBin * toGstBin (void *p); GstBus * toGstBus (void *p); GstMessage * toGstMessage (void *p); @@ -63,3 +59,4 @@ GstCaps * toGstCaps (void *p); GstCapsFeatures * toGstCapsFeatures (void *p); GstBuffer * toGstBuffer (void *p); GstBufferPool * toGstBufferPool (void *p); +GstSample * toGstSample (void *p); \ No newline at end of file diff --git a/gst/gst_bin.go b/gst/gst_bin.go index 89daf33..88a09f2 100644 --- a/gst/gst_bin.go +++ b/gst/gst_bin.go @@ -3,6 +3,7 @@ package gst // #include "gst.go.h" import "C" import ( + "errors" "fmt" "unsafe" @@ -13,7 +14,7 @@ import ( type Bin struct{ *Element } // Instance returns the underlying GstBin instance. -func (b *Bin) Instance() *C.GstBin { return C.toGstBin(b.unsafe()) } +func (b *Bin) Instance() *C.GstBin { return C.toGstBin(b.Unsafe()) } // GetElementByName returns the element with the given name. Unref after usage. func (b *Bin) GetElementByName(name string) (*Element, error) { @@ -47,7 +48,7 @@ func (b *Bin) GetSinkElements() ([]*Element, error) { // Add wraps `gst_bin_add`. func (b *Bin) Add(elem *Element) error { - if ok := C.gst_bin_add((*C.GstBin)(b.Instance()), (*C.GstElement)(elem.Instance())); !gobool(ok) { + if ok := C.gst_bin_add((*C.GstBin)(b.Instance()), (*C.GstElement)(elem.Instance())); gobool(ok) { return fmt.Errorf("Failed to add element to pipeline: %s", elem.Name()) } return nil @@ -63,3 +64,25 @@ func (b *Bin) AddMany(elems ...*Element) error { } return nil } + +func iteratorToElementSlice(iterator *C.GstIterator) ([]*Element, error) { + elems := make([]*Element, 0) + gval := new(C.GValue) + + for { + switch C.gst_iterator_next((*C.GstIterator)(iterator), (*C.GValue)(unsafe.Pointer(gval))) { + case C.GST_ITERATOR_DONE: + C.gst_iterator_free((*C.GstIterator)(iterator)) + return elems, nil + case C.GST_ITERATOR_RESYNC: + C.gst_iterator_resync((*C.GstIterator)(iterator)) + case C.GST_ITERATOR_OK: + cElemVoid := C.g_value_get_object((*C.GValue)(gval)) + cElem := (*C.GstElement)(cElemVoid) + elems = append(elems, wrapElement(glib.Take(unsafe.Pointer(cElem)))) + C.g_value_reset((*C.GValue)(gval)) + default: + return nil, errors.New("Element iterator failed") + } + } +} diff --git a/gst/gst_bus.go b/gst/gst_bus.go index 8abe92e..b981b5d 100644 --- a/gst/gst_bus.go +++ b/gst/gst_bus.go @@ -18,7 +18,7 @@ type Bus struct { } // Instance returns the underlying GstBus instance. -func (b *Bus) Instance() *C.GstBus { return C.toGstBus(b.unsafe()) } +func (b *Bus) Instance() *C.GstBus { return C.toGstBus(b.Unsafe()) } func (b *Bus) deliverMessages() { for { diff --git a/gst/gst_clock.go b/gst/gst_clock.go index e47619d..1277600 100644 --- a/gst/gst_clock.go +++ b/gst/gst_clock.go @@ -8,7 +8,7 @@ import "time" type Clock struct{ *Object } // Instance returns the underlying GstClock instance. -func (c *Clock) Instance() *C.GstClock { return C.toGstClock(c.unsafe()) } +func (c *Clock) Instance() *C.GstClock { return C.toGstClock(c.Unsafe()) } // IsSynced returns true if the clock is synced. func (c *Clock) IsSynced() bool { return gobool(C.gst_clock_is_synced(c.Instance())) } diff --git a/gst/gst_element.go b/gst/gst_element.go index 1986d00..0e75c93 100644 --- a/gst/gst_element.go +++ b/gst/gst_element.go @@ -29,7 +29,7 @@ func ElementLinkMany(elems ...*Element) error { } // Instance returns the underlying GstElement instance. -func (e *Element) Instance() *C.GstElement { return C.toGstElement(e.unsafe()) } +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 { @@ -137,7 +137,7 @@ func (e *Element) GetPadTemplates() []*PadTemplate { // 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))) + return gobool(C.gstObjectFlagIsSet(C.toGstObject(e.Unsafe()), C.GstElementFlags(flags))) } // IsURIHandler returns true if this element can handle URIs. @@ -145,7 +145,7 @@ func (e *Element) IsURIHandler() bool { return gobool(C.gstElementIsURIHandler(e.Instance())) } -func (e *Element) uriHandler() *C.GstURIHandler { return C.toGstURIHandler(e.unsafe()) } +func (e *Element) uriHandler() *C.GstURIHandler { return C.toGstURIHandler(e.Unsafe()) } // GetURIType returns the type of URI this element can handle. func (e *Element) GetURIType() URIType { diff --git a/gst/gst_element_factory.go b/gst/gst_element_factory.go index 22b8675..e30f08c 100644 --- a/gst/gst_element_factory.go +++ b/gst/gst_element_factory.go @@ -51,7 +51,7 @@ func Find(name string) *ElementFactory { } // Instance returns the C GstFactory instance -func (e *ElementFactory) Instance() *C.GstElementFactory { return C.toGstElementFactory(e.unsafe()) } +func (e *ElementFactory) Instance() *C.GstElementFactory { return C.toGstElementFactory(e.Unsafe()) } // CanSinkAllCaps checks if the factory can sink all possible capabilities. func (e *ElementFactory) CanSinkAllCaps(caps *C.GstCaps) bool { diff --git a/gst/gst_message.go b/gst/gst_message.go index 82b3306..3ee37ed 100644 --- a/gst/gst_message.go +++ b/gst/gst_message.go @@ -14,12 +14,12 @@ type Message struct { msg *C.GstMessage } -// Native returns the underlying GstMessage object. -func (m *Message) Native() *C.GstMessage { return C.toGstMessage(unsafe.Pointer(m.msg)) } +// Instance returns the underlying GstMessage object. +func (m *Message) Instance() *C.GstMessage { return C.toGstMessage(unsafe.Pointer(m.msg)) } // Type returns the MessageType of the message. func (m *Message) Type() MessageType { - return MessageType(m.Native()._type) + return MessageType(m.Instance()._type) } // TypeName returns a Go string of the GstMessageType name. @@ -34,11 +34,11 @@ func (m *Message) GetStructure() *Structure { switch m.Type() { case MessageError: - C.gst_message_parse_error_details((*C.GstMessage)(m.Native()), (**C.GstStructure)(unsafe.Pointer(&st))) + C.gst_message_parse_error_details((*C.GstMessage)(m.Instance()), (**C.GstStructure)(unsafe.Pointer(&st))) case MessageInfo: - C.gst_message_parse_info_details((*C.GstMessage)(m.Native()), (**C.GstStructure)(unsafe.Pointer(&st))) + C.gst_message_parse_info_details((*C.GstMessage)(m.Instance()), (**C.GstStructure)(unsafe.Pointer(&st))) case MessageWarning: - C.gst_message_parse_warning_details((*C.GstMessage)(m.Native()), (**C.GstStructure)(unsafe.Pointer(&st))) + C.gst_message_parse_warning_details((*C.GstMessage)(m.Instance()), (**C.GstStructure)(unsafe.Pointer(&st))) } // if no structure was returned, immediately return nil @@ -59,11 +59,11 @@ func (m *Message) parseToError() *GoGError { switch m.Type() { case MessageError: - C.gst_message_parse_error((*C.GstMessage)(m.Native()), (**C.GError)(unsafe.Pointer(&gerr)), (**C.gchar)(unsafe.Pointer(&debugInfo))) + C.gst_message_parse_error((*C.GstMessage)(m.Instance()), (**C.GError)(unsafe.Pointer(&gerr)), (**C.gchar)(unsafe.Pointer(&debugInfo))) case MessageInfo: - C.gst_message_parse_info((*C.GstMessage)(m.Native()), (**C.GError)(unsafe.Pointer(&gerr)), (**C.gchar)(unsafe.Pointer(&debugInfo))) + C.gst_message_parse_info((*C.GstMessage)(m.Instance()), (**C.GError)(unsafe.Pointer(&gerr)), (**C.gchar)(unsafe.Pointer(&debugInfo))) case MessageWarning: - C.gst_message_parse_warning((*C.GstMessage)(m.Native()), (**C.GError)(unsafe.Pointer(&gerr)), (**C.gchar)(unsafe.Pointer(&debugInfo))) + C.gst_message_parse_warning((*C.GstMessage)(m.Instance()), (**C.GError)(unsafe.Pointer(&gerr)), (**C.gchar)(unsafe.Pointer(&debugInfo))) } // if error was nil return immediately @@ -104,26 +104,26 @@ func (m *Message) ParseError() *GoGError { // if the GstMessageType is `GST_MESSAGE_STATE_CHANGED`. func (m *Message) ParseStateChanged() (oldState, newState State) { var gOldState, gNewState C.GstState - C.gst_message_parse_state_changed((*C.GstMessage)(m.Native()), (*C.GstState)(unsafe.Pointer(&gOldState)), (*C.GstState)(unsafe.Pointer(&gNewState)), nil) + C.gst_message_parse_state_changed((*C.GstMessage)(m.Instance()), (*C.GstState)(unsafe.Pointer(&gOldState)), (*C.GstState)(unsafe.Pointer(&gNewState)), nil) oldState = State(gOldState) newState = State(gNewState) return } // Unref will call `gst_message_unref` on the underlying GstMessage, freeing it from memory. -func (m *Message) Unref() { C.gst_message_unref((*C.GstMessage)(m.Native())) } +func (m *Message) Unref() { C.gst_message_unref((*C.GstMessage)(m.Instance())) } // Ref will increase the ref count on this message. This increases the total amount of times // Unref needs to be called before the object is freed from memory. It returns the underlying // message object for convenience. func (m *Message) Ref() *Message { - C.gst_message_ref((*C.GstMessage)(m.Native())) + C.gst_message_ref((*C.GstMessage)(m.Instance())) return m } // Copy will copy this object into a new Message that can be Unrefed separately. func (m *Message) Copy() *Message { - newNative := C.gst_message_copy((*C.GstMessage)(m.Native())) + newNative := C.gst_message_copy((*C.GstMessage)(m.Instance())) return wrapMessage(newNative) } diff --git a/gst/gst_object.go b/gst/gst_object.go index 61ffc7a..242694a 100644 --- a/gst/gst_object.go +++ b/gst/gst_object.go @@ -13,14 +13,15 @@ import ( // and we do not descend into the glib library. type Object struct{ *glib.InitiallyUnowned } -// native returns the pointer to the underlying object. -func (o *Object) unsafe() unsafe.Pointer { return unsafe.Pointer(o.InitiallyUnowned.Native()) } +// Unsafe returns the unsafe pointer to the underlying object. This method is primarily +// for internal usage and is exposed for visibility in other packages. +func (o *Object) Unsafe() unsafe.Pointer { return unsafe.Pointer(o.InitiallyUnowned.Native()) } // Instance returns the native C GstObject. -func (o *Object) Instance() *C.GstObject { return C.toGstObject(o.unsafe()) } +func (o *Object) Instance() *C.GstObject { return C.toGstObject(o.Unsafe()) } // Class returns the GObjectClass of this instance. -func (o *Object) Class() *C.GObjectClass { return C.getGObjectClass(o.unsafe()) } +func (o *Object) Class() *C.GObjectClass { return C.getGObjectClass(o.Unsafe()) } // Name returns the name of this object. func (o *Object) Name() string { @@ -61,7 +62,7 @@ func (o *Object) ListProperties() []*ParameterSpec { var gval C.GValue flags := ParameterFlags(prop.flags) if flags.Has(ParameterReadable) { - C.g_object_get_property((*C.GObject)(o.unsafe()), prop.name, &gval) + C.g_object_get_property((*C.GObject)(o.Unsafe()), prop.name, &gval) } else { C.g_param_value_set_default((*C.GParamSpec)(prop), &gval) } diff --git a/gst/gst_pad.go b/gst/gst_pad.go index a130368..d310cae 100644 --- a/gst/gst_pad.go +++ b/gst/gst_pad.go @@ -12,7 +12,7 @@ import ( type Pad struct{ *Object } // Instance returns the underlying C GstPad. -func (p *Pad) Instance() *C.GstPad { return C.toGstPad(p.unsafe()) } +func (p *Pad) Instance() *C.GstPad { return C.toGstPad(p.Unsafe()) } // Direction returns the direction of this pad. func (p *Pad) Direction() PadDirection { @@ -37,7 +37,7 @@ func (p *Pad) CurrentCaps() *Caps { type PadTemplate struct{ *Object } // Instance returns the underlying C GstPadTemplate. -func (p *PadTemplate) Instance() *C.GstPadTemplate { return C.toGstPadTemplate(p.unsafe()) } +func (p *PadTemplate) Instance() *C.GstPadTemplate { return C.toGstPadTemplate(p.Unsafe()) } // Name returns the name of the pad template. func (p *PadTemplate) Name() string { return C.GoString(p.Instance().name_template) } diff --git a/gst/gst_pipeline.go b/gst/gst_pipeline.go index 445a558..fcaa13f 100644 --- a/gst/gst_pipeline.go +++ b/gst/gst_pipeline.go @@ -51,7 +51,7 @@ func NewPipelineFromString(launchv string) (*Pipeline, error) { } // Instance returns the native GstPipeline instance. -func (p *Pipeline) Instance() *C.GstPipeline { return C.toGstPipeline(p.unsafe()) } +func (p *Pipeline) Instance() *C.GstPipeline { return C.toGstPipeline(p.Unsafe()) } // GetPipelineBus returns the message bus for this pipeline. func (p *Pipeline) GetPipelineBus() *Bus { diff --git a/gst/gst_plugin.go b/gst/gst_plugin.go index 99c646d..49bf331 100644 --- a/gst/gst_plugin.go +++ b/gst/gst_plugin.go @@ -7,7 +7,7 @@ import "C" type Plugin struct{ *Object } // Instance returns the underlying GstPlugin instance. -func (p *Plugin) Instance() *C.GstPlugin { return C.toGstPlugin(p.unsafe()) } +func (p *Plugin) Instance() *C.GstPlugin { return C.toGstPlugin(p.Unsafe()) } // Description returns the description for this plugin. func (p *Plugin) Description() string { diff --git a/gst/gst_plugin_feature.go b/gst/gst_plugin_feature.go index 898d0d4..a17faf2 100644 --- a/gst/gst_plugin_feature.go +++ b/gst/gst_plugin_feature.go @@ -12,7 +12,7 @@ import ( type PluginFeature struct{ *Object } // Instance returns the underlying GstPluginFeature instance -func (p *PluginFeature) Instance() *C.GstPluginFeature { return C.toGstPluginFeature(p.unsafe()) } +func (p *PluginFeature) Instance() *C.GstPluginFeature { return C.toGstPluginFeature(p.Unsafe()) } // GetPlugin returns the plugin that provides this feature or nil. Unref after usage. func (p *PluginFeature) GetPlugin() *Plugin { diff --git a/gst/gst_registry.go b/gst/gst_registry.go index fedf7af..869df8b 100644 --- a/gst/gst_registry.go +++ b/gst/gst_registry.go @@ -19,7 +19,7 @@ func GetRegistry() *Registry { } // Instance returns the underlying GstRegistry instance. -func (r *Registry) Instance() *C.GstRegistry { return C.toGstRegistry(r.unsafe()) } +func (r *Registry) Instance() *C.GstRegistry { return C.toGstRegistry(r.Unsafe()) } // FindPlugin retrieves the plugin by the given name. Unref after usage. func (r *Registry) FindPlugin(name string) (*Plugin, error) { diff --git a/gst/gst_sample.go b/gst/gst_sample.go index cac02c2..dde5470 100644 --- a/gst/gst_sample.go +++ b/gst/gst_sample.go @@ -2,14 +2,19 @@ package gst // #include "gst.go.h" import "C" +import "unsafe" // Sample is a go wrapper around a GstSample object. type Sample struct { sample *C.GstSample } +// FromGstSampleUnsafe wraps the pointer to the given C GstSample with the go type. +// This is meant for internal usage and is exported for visibility to other packages. +func FromGstSampleUnsafe(sample unsafe.Pointer) *Sample { return wrapSample(C.toGstSample(sample)) } + // Instance returns the underlying *GstSample instance. -func (s *Sample) Instance() *C.GstSample { return s.sample } +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())) } diff --git a/gst/gst_wrappers.go b/gst/gst_wrappers.go index cd3b6fc..3e2066b 100644 --- a/gst/gst_wrappers.go +++ b/gst/gst_wrappers.go @@ -113,9 +113,6 @@ func wrapGhostPad(obj *glib.Object) *GhostPad { return &GhostPad{wrapPad(o func wrapPlugin(obj *glib.Object) *Plugin { return &Plugin{wrapObject(obj)} } func wrapRegistry(obj *glib.Object) *Registry { return &Registry{wrapObject(obj)} } -func wrapAppSink(elem *Element) *AppSink { return &AppSink{elem} } -func wrapAppSrc(elem *Element) *AppSrc { return &AppSrc{elem} } - func wrapSample(sample *C.GstSample) *Sample { return &Sample{sample: sample} } func wrapBuffer(buf *C.GstBuffer) *Buffer { return &Buffer{ptr: buf} } diff --git a/gst/gstauto/app/doc.go b/gst/gstauto/app/doc.go new file mode 100644 index 0000000..744ea9e --- /dev/null +++ b/gst/gstauto/app/doc.go @@ -0,0 +1,7 @@ +// Package app provides interfaces from the gstauto package that +// use the gstreamer-app library for interacting with either end +// of the pipeline. +// +// Using this package will require additional dependencies at build and runtime. +// See the README in the main repo for details. +package app diff --git a/gst/gstauto/pipeline_app_reader.go b/gst/gstauto/app/pipeline_app_reader.go similarity index 70% rename from gst/gstauto/pipeline_app_reader.go rename to gst/gstauto/app/pipeline_app_reader.go index e45ef5a..ec3e97d 100644 --- a/gst/gstauto/pipeline_app_reader.go +++ b/gst/gstauto/app/pipeline_app_reader.go @@ -1,4 +1,4 @@ -package gstauto +package app import ( "errors" @@ -6,15 +6,17 @@ import ( "strings" "github.com/tinyzimmer/go-gst/gst" + "github.com/tinyzimmer/go-gst/gst/app" + "github.com/tinyzimmer/go-gst/gst/gstauto" ) // PipelineReaderApp implements a ReadPipeliner that configures gstreamer // with an appsink. The appsink allows for more granular control over the data // at the end of the pipeline. type PipelineReaderApp struct { - *PipelineReader + *gstauto.PipelineReader - appSink *gst.AppSink + appSink *app.Sink } // NewPipelineReaderAppFromString returns a new PipelineReaderApp populated from @@ -22,32 +24,25 @@ type PipelineReaderApp struct { // available via the GetAppSink method. func NewPipelineReaderAppFromString(launchStr string) (*PipelineReaderApp, error) { fmt.Println(addAppSinkToStr(launchStr)) - pipelineReader, err := NewPipelineReaderFromString(addAppSinkToStr(launchStr)) + pipelineReader, err := gstauto.NewPipelineReaderFromString(addAppSinkToStr(launchStr)) if err != nil { return nil, err } - defer func() { - if err != nil { - if destroyErr := pipelineReader.Pipeline().Destroy(); destroyErr != nil { - fmt.Println("[go-gst] Error while destroying failed pipeline instance:", destroyErr.Error()) - } - } - }() - appPipeline := &PipelineReaderApp{PipelineReader: pipelineReader} // Retrieve the sinks in the pipeline, most of the time there is just one var sinks []*gst.Element sinks, err = pipelineReader.Pipeline().GetSinkElements() if err != nil { + runOrPrintErr(pipelineReader.Pipeline().Destroy) return nil, err } // Fetch the appsink and make a local reference to it for _, sink := range sinks { if strings.Contains(sink.Name(), "appsink") { - appPipeline.appSink = &gst.AppSink{Element: sink} + appPipeline.appSink = &app.Sink{Element: sink} } } @@ -58,24 +53,22 @@ func NewPipelineReaderAppFromString(launchStr string) (*PipelineReaderApp, error // NewPipelineReaderAppFromConfig returns a new PipelineReaderApp populated from // the given launch config. An appsink is added to the end of the launch config and // made available via the GetAppSink method. -func NewPipelineReaderAppFromConfig(cfg *PipelineConfig) (*PipelineReaderApp, error) { +func NewPipelineReaderAppFromConfig(cfg *gstauto.PipelineConfig) (*PipelineReaderApp, error) { if cfg.Elements == nil { return nil, errors.New("Elements cannot be nil in the config") } - pipelineReader, err := NewPipelineReader("") + pipelineReader, err := gstauto.NewPipelineReader("") if err != nil { return nil, err } defer func() { if err != nil { - if destroyErr := pipelineReader.Pipeline().Destroy(); destroyErr != nil { - fmt.Println("[go-gst] Error while destroying failed pipeline instance:", destroyErr.Error()) - } + runOrPrintErr(pipelineReader.Pipeline().Destroy) } }() - cfg.Elements = append(cfg.Elements, &PipelineElement{Name: "appsink"}) + cfg.Elements = append(cfg.Elements, &gstauto.PipelineElement{Name: "appsink"}) if err = cfg.Apply(pipelineReader.Pipeline()); err != nil { return nil, err @@ -93,7 +86,7 @@ func NewPipelineReaderAppFromConfig(cfg *PipelineConfig) (*PipelineReaderApp, er // Fetch the appsink and make a local reference to it for _, sink := range sinks { if strings.Contains(sink.Name(), "appsink") { - appPipeline.appSink = &gst.AppSink{Element: sink} + appPipeline.appSink = &app.Sink{Element: sink} } } @@ -108,4 +101,4 @@ func addAppSinkToStr(pstr string) string { } // GetAppSink returns the app sink for this pipeline. -func (p *PipelineReaderApp) GetAppSink() *gst.AppSink { return p.appSink } +func (p *PipelineReaderApp) GetAppSink() *app.Sink { return p.appSink } diff --git a/gst/gstauto/pipeline_app_readwriter.go b/gst/gstauto/app/pipeline_app_readwriter.go similarity index 73% rename from gst/gstauto/pipeline_app_readwriter.go rename to gst/gstauto/app/pipeline_app_readwriter.go index b4f75a1..1092b5a 100644 --- a/gst/gstauto/pipeline_app_readwriter.go +++ b/gst/gstauto/app/pipeline_app_readwriter.go @@ -1,11 +1,12 @@ -package gstauto +package app import ( "errors" - "fmt" "strings" "github.com/tinyzimmer/go-gst/gst" + "github.com/tinyzimmer/go-gst/gst/app" + "github.com/tinyzimmer/go-gst/gst/gstauto" ) // PipelineReadWriterApp implements a ReadPipeliner that configures gstreamer @@ -13,10 +14,10 @@ import ( // at the end of the pipeline, and the appsrc allows for control over the data // at the start. type PipelineReadWriterApp struct { - *PipelineReadWriter + *gstauto.PipelineReadWriter - appSrc *gst.AppSrc - appSink *gst.AppSink + appSrc *app.Source + appSink *app.Sink } // NewPipelineReadWriterAppFromString returns a new PipelineReadWriterApp populated from @@ -24,16 +25,14 @@ type PipelineReadWriterApp struct { // available via the GetAppSink method, and an appsrc is added to the end and made // available via the GetAppSource method. func NewPipelineReadWriterAppFromString(launchStr string) (*PipelineReadWriterApp, error) { - pipelineReadWriter, err := NewPipelineReadWriterFromString(addAppSourceToStr(addAppSinkToStr(launchStr))) + pipelineReadWriter, err := gstauto.NewPipelineReadWriterFromString(addAppSourceToStr(addAppSinkToStr(launchStr))) if err != nil { return nil, err } defer func() { if err != nil { - if destroyErr := pipelineReadWriter.Pipeline().Destroy(); destroyErr != nil { - fmt.Println("[go-gst] Error while destroying failed pipeline instance:", destroyErr.Error()) - } + runOrPrintErr(pipelineReadWriter.Pipeline().Destroy) } }() @@ -49,7 +48,7 @@ func NewPipelineReadWriterAppFromString(launchStr string) (*PipelineReadWriterAp // Fetch the appsrc and make a local reference to it for _, src := range sources { if strings.Contains(src.Name(), "appsrc") { - appPipeline.appSrc = &gst.AppSrc{Element: src} + appPipeline.appSrc = &app.Source{Element: src} } } @@ -63,7 +62,7 @@ func NewPipelineReadWriterAppFromString(launchStr string) (*PipelineReadWriterAp // Fetch the appsink and make a local reference to it for _, sink := range sinks { if strings.Contains(sink.Name(), "appsink") { - appPipeline.appSink = &gst.AppSink{Element: sink} + appPipeline.appSink = &app.Sink{Element: sink} } } @@ -75,24 +74,22 @@ func NewPipelineReadWriterAppFromString(launchStr string) (*PipelineReadWriterAp // the given launch config. An appsink is added to the end of the launch config and // made available via the GetAppSink method, and an appsrc is added at the front and made // available via the GetAppSource method. -func NewPipelineReadWriterAppFromConfig(cfg *PipelineConfig) (*PipelineReadWriterApp, error) { +func NewPipelineReadWriterAppFromConfig(cfg *gstauto.PipelineConfig) (*PipelineReadWriterApp, error) { if cfg.Elements == nil { return nil, errors.New("Elements cannot be nil in the config") } - pipelineReadWriter, err := NewPipelineReadWriter("") + pipelineReadWriter, err := gstauto.NewPipelineReadWriter("") if err != nil { return nil, err } defer func() { if err != nil { - if destroyErr := pipelineReadWriter.Pipeline().Destroy(); destroyErr != nil { - fmt.Println("[go-gst] Error while destroying failed pipeline instance:", destroyErr.Error()) - } + runOrPrintErr(pipelineReadWriter.Pipeline().Destroy) } }() - cfg.Elements = append(cfg.Elements, &PipelineElement{Name: "appsink"}) + cfg.Elements = append(cfg.Elements, &gstauto.PipelineElement{Name: "appsink"}) if err = cfg.Apply(pipelineReadWriter.Pipeline()); err != nil { return nil, err @@ -110,7 +107,7 @@ func NewPipelineReadWriterAppFromConfig(cfg *PipelineConfig) (*PipelineReadWrite // Fetch the appsrc and make a local reference to it for _, src := range sources { if strings.Contains(src.Name(), "appsrc") { - appPipeline.appSrc = &gst.AppSrc{Element: src} + appPipeline.appSrc = &app.Source{Element: src} } } @@ -124,7 +121,7 @@ func NewPipelineReadWriterAppFromConfig(cfg *PipelineConfig) (*PipelineReadWrite // Fetch the appsink and make a local reference to it for _, sink := range sinks { if strings.Contains(sink.Name(), "appsink") { - appPipeline.appSink = &gst.AppSink{Element: sink} + appPipeline.appSink = &app.Sink{Element: sink} } } @@ -132,7 +129,7 @@ func NewPipelineReadWriterAppFromConfig(cfg *PipelineConfig) (*PipelineReadWrite } // GetAppSink returns the app sink for this pipeline. -func (p *PipelineReadWriterApp) GetAppSink() *gst.AppSink { return p.appSink } +func (p *PipelineReadWriterApp) GetAppSink() *app.Sink { return p.appSink } // GetAppSource returns the app src for this pipeline. -func (p *PipelineReadWriterApp) GetAppSource() *gst.AppSrc { return p.appSrc } +func (p *PipelineReadWriterApp) GetAppSource() *app.Source { return p.appSrc } diff --git a/gst/gstauto/pipeline_app_writer.go b/gst/gstauto/app/pipeline_app_writer.go similarity index 71% rename from gst/gstauto/pipeline_app_writer.go rename to gst/gstauto/app/pipeline_app_writer.go index 26769fc..bf65ca0 100644 --- a/gst/gstauto/pipeline_app_writer.go +++ b/gst/gstauto/app/pipeline_app_writer.go @@ -1,4 +1,4 @@ -package gstauto +package app import ( "errors" @@ -6,47 +6,42 @@ import ( "strings" "github.com/tinyzimmer/go-gst/gst" + "github.com/tinyzimmer/go-gst/gst/app" + "github.com/tinyzimmer/go-gst/gst/gstauto" ) // PipelineWriterApp implements a WritePipeliner that configures gstreamer // with an appsrc. The appsrc allows for more granular control over the data // at the start of the pipeline. type PipelineWriterApp struct { - *PipelineWriter + *gstauto.PipelineWriter - appSrc *gst.AppSrc + appSrc *app.Source } // NewPipelineWriterAppFromString returns a new PipelineWriterApp populated from // the given launch string. An appsrc is added to the start of the launch string and made // available via the GetAppSource method. func NewPipelineWriterAppFromString(launchStr string) (*PipelineWriterApp, error) { - pipelineWriter, err := NewPipelineWriterFromString(addAppSourceToStr(launchStr)) + pipelineWriter, err := gstauto.NewPipelineWriterFromString(addAppSourceToStr(launchStr)) if err != nil { return nil, err } - defer func() { - if err != nil { - if destroyErr := pipelineWriter.Pipeline().Destroy(); destroyErr != nil { - fmt.Println("[go-gst] Error while destroying failed pipeline instance:", destroyErr.Error()) - } - } - }() - appPipeline := &PipelineWriterApp{PipelineWriter: pipelineWriter} // Retrieve the sources in the pipeline, most of the time there is just one var sources []*gst.Element sources, err = pipelineWriter.Pipeline().GetSourceElements() if err != nil { + runOrPrintErr(pipelineWriter.Pipeline().Destroy) return nil, err } // Fetch the appsrc and make a local reference to it for _, src := range sources { if strings.Contains(src.Name(), "appsrc") { - appPipeline.appSrc = &gst.AppSrc{Element: src} + appPipeline.appSrc = &app.Source{Element: src} } } @@ -57,24 +52,22 @@ func NewPipelineWriterAppFromString(launchStr string) (*PipelineWriterApp, error // NewPipelineWriterAppFromConfig returns a new PipelineWriterApp populated from // the given launch config. An appsrc is added to the start of the launch config and // made available via the GetAppSource method. -func NewPipelineWriterAppFromConfig(cfg *PipelineConfig) (*PipelineWriterApp, error) { +func NewPipelineWriterAppFromConfig(cfg *gstauto.PipelineConfig) (*PipelineWriterApp, error) { if cfg.Elements == nil { return nil, errors.New("Elements cannot be nil in the config") } - pipelineWriter, err := NewPipelineWriter("") + pipelineWriter, err := gstauto.NewPipelineWriter("") if err != nil { return nil, err } defer func() { if err != nil { - if destroyErr := pipelineWriter.Pipeline().Destroy(); destroyErr != nil { - fmt.Println("[go-gst] Error while destroying failed pipeline instance:", destroyErr.Error()) - } + runOrPrintErr(pipelineWriter.Pipeline().Destroy) } }() - cfg.pushPluginToTop(&PipelineElement{Name: "appsrc"}) + cfg.PushPluginToTop(&gstauto.PipelineElement{Name: "appsrc"}) if err = cfg.Apply(pipelineWriter.Pipeline()); err != nil { return nil, err @@ -92,7 +85,7 @@ func NewPipelineWriterAppFromConfig(cfg *PipelineConfig) (*PipelineWriterApp, er // Fetch the appsrc and make a local reference to it for _, src := range sources { if strings.Contains(src.Name(), "appsrc") { - appPipeline.appSrc = &gst.AppSrc{Element: src} + appPipeline.appSrc = &app.Source{Element: src} } } @@ -107,4 +100,4 @@ func addAppSourceToStr(pstr string) string { } // GetAppSource returns the app src for this pipeline. -func (p *PipelineWriterApp) GetAppSource() *gst.AppSrc { return p.appSrc } +func (p *PipelineWriterApp) GetAppSource() *app.Source { return p.appSrc } diff --git a/gst/gstauto/app/util.go b/gst/gstauto/app/util.go new file mode 100644 index 0000000..ada5070 --- /dev/null +++ b/gst/gstauto/app/util.go @@ -0,0 +1,9 @@ +package app + +import "fmt" + +func runOrPrintErr(f func() error) { + if err := f(); err != nil { + fmt.Println("[go-gst/gst/gstauto] Internal Error:", err.Error()) + } +} diff --git a/gst/gstauto/pipeline_config.go b/gst/gstauto/pipeline_config.go index 6a46f3a..6470329 100644 --- a/gst/gstauto/pipeline_config.go +++ b/gst/gstauto/pipeline_config.go @@ -2,7 +2,6 @@ package gstauto import ( "errors" - "fmt" "github.com/tinyzimmer/go-gst/gst" ) @@ -45,8 +44,8 @@ func (p *PipelineConfig) ElementNames() []string { return names } -// pushPluginToTop pushes a plugin to the top of the list. -func (p *PipelineConfig) pushPluginToTop(elem *PipelineElement) { +// PushPluginToTop pushes a plugin to the top of the list of elements. +func (p *PipelineConfig) PushPluginToTop(elem *PipelineElement) { newSlc := []*PipelineElement{elem} newSlc = append(newSlc, p.Elements...) p.Elements = newSlc @@ -124,16 +123,8 @@ func NewPipelineFromConfig(cfg *PipelineConfig) (*gst.Pipeline, error) { return nil, err } - // if any error happens while setting up the pipeline, immediately free it - defer func() { - if err != nil { - if destroyErr := pipeline.Destroy(); destroyErr != nil { - fmt.Println("[go-gst] Error while destroying failed pipeline instance:", err.Error()) - } - } - }() - if err = cfg.Apply(pipeline); err != nil { + runOrPrintErr(pipeline.Destroy) return nil, err } diff --git a/gst/gstauto/pipeline_reader.go b/gst/gstauto/pipeline_reader.go index 944cfee..1333809 100644 --- a/gst/gstauto/pipeline_reader.go +++ b/gst/gstauto/pipeline_reader.go @@ -1,8 +1,6 @@ package gstauto import ( - "fmt" - "github.com/tinyzimmer/go-gst/gst" ) @@ -25,9 +23,7 @@ func NewPipelineReader(name string) (*PipelineReader, error) { } rCloser, err := newReadCloser() if err != nil { - if closeErr := pipeline.Destroy(); closeErr != nil { - fmt.Println("[gst-auto] Failed to destroy errored pipeline:", closeErr.Error()) - } + runOrPrintErr(pipeline.Destroy) return nil, err } return &PipelineReader{ @@ -46,9 +42,7 @@ func NewPipelineReaderFromString(launchStr string) (*PipelineReader, error) { } rCloser, err := newReadCloser() if err != nil { - if closeErr := pipeline.Destroy(); closeErr != nil { - fmt.Println("[gst-auto] Failed to destroy errored pipeline:", closeErr.Error()) - } + runOrPrintErr(pipeline.Destroy) return nil, err } return &PipelineReader{ diff --git a/gst/gstauto/pipeline_readwriter.go b/gst/gstauto/pipeline_readwriter.go index ebe5e4f..e9b97b5 100644 --- a/gst/gstauto/pipeline_readwriter.go +++ b/gst/gstauto/pipeline_readwriter.go @@ -1,8 +1,6 @@ package gstauto import ( - "fmt" - "github.com/tinyzimmer/go-gst/gst" ) @@ -25,9 +23,7 @@ func NewPipelineReadWriter(name string) (*PipelineReadWriter, error) { } rwCloser, err := newReadWriteCloser() if err != nil { - if closeErr := pipeline.Destroy(); closeErr != nil { - fmt.Println("[gst-auto] Failed to destroy errored pipeline:", closeErr.Error()) - } + runOrPrintErr(pipeline.Destroy) return nil, err } return &PipelineReadWriter{ @@ -46,9 +42,7 @@ func NewPipelineReadWriterFromString(launchStr string) (*PipelineReadWriter, err } rwCloser, err := newReadWriteCloser() if err != nil { - if closeErr := pipeline.Destroy(); closeErr != nil { - fmt.Println("[gst-auto] Failed to destroy errored pipeline:", closeErr.Error()) - } + runOrPrintErr(pipeline.Destroy) return nil, err } return &PipelineReadWriter{ diff --git a/gst/gstauto/pipeline_simple.go b/gst/gstauto/pipeline_simple.go index 4506f03..e013c49 100644 --- a/gst/gstauto/pipeline_simple.go +++ b/gst/gstauto/pipeline_simple.go @@ -1,8 +1,6 @@ package gstauto import ( - "fmt" - "github.com/tinyzimmer/go-gst/gst" ) @@ -48,9 +46,7 @@ func NewPipelinerSimpleFromConfig(cfg *PipelineConfig) (*PipelinerSimple, error) return nil, err } if err := cfg.Apply(pipeline); err != nil { - if destroyErr := pipeline.Destroy(); destroyErr != nil { - fmt.Println("[go-gst] Error while destroying failed pipeline instance:", destroyErr.Error()) - } + runOrPrintErr(pipeline.Destroy) return nil, err } return &PipelinerSimple{pipeline: pipeline}, nil diff --git a/gst/gstauto/pipeline_simple_reader.go b/gst/gstauto/pipeline_simple_reader.go index b3adc0a..9d6d904 100644 --- a/gst/gstauto/pipeline_simple_reader.go +++ b/gst/gstauto/pipeline_simple_reader.go @@ -25,9 +25,7 @@ func NewPipelineReaderSimpleFromString(launchStr string) (*PipelineReaderSimple, defer func() { if err != nil { - if destroyErr := pipelineReader.Pipeline().Destroy(); destroyErr != nil { - fmt.Println("[go-gst] Error while destroying failed pipeline instance:", destroyErr.Error()) - } + runOrPrintErr(pipelineReader.Pipeline().Destroy) } }() @@ -69,9 +67,7 @@ func NewPipelineReaderSimpleFromConfig(cfg *PipelineConfig) (*PipelineReaderSimp }, }) if err := cfg.Apply(pipelineReader.Pipeline()); err != nil { - if destroyErr := pipelineReader.Pipeline().Destroy(); destroyErr != nil { - fmt.Println("[go-gst] Error while destroying failed pipeline instance:", destroyErr.Error()) - } + runOrPrintErr(pipelineReader.Pipeline().Destroy) return nil, err } return &PipelineReaderSimple{pipelineReader}, nil diff --git a/gst/gstauto/pipeline_simple_readwriter.go b/gst/gstauto/pipeline_simple_readwriter.go index f87281c..e02a0b9 100644 --- a/gst/gstauto/pipeline_simple_readwriter.go +++ b/gst/gstauto/pipeline_simple_readwriter.go @@ -2,7 +2,6 @@ package gstauto import ( "errors" - "fmt" "strings" "github.com/tinyzimmer/go-gst/gst" @@ -26,9 +25,7 @@ func NewPipelineReadWriterSimpleFromString(launchStr string) (*PipelineReadWrite defer func() { if err != nil { - if destroyErr := pipelineReadWriter.Pipeline().Destroy(); destroyErr != nil { - fmt.Println("[go-gst] Error while destroying failed pipeline instance:", destroyErr.Error()) - } + runOrPrintErr(pipelineReadWriter.Pipeline().Destroy) } }() @@ -79,7 +76,7 @@ func NewPipelineReadWriterSimpleFromConfig(cfg *PipelineConfig) (*PipelineReadWr if err != nil { return nil, err } - cfg.pushPluginToTop(&PipelineElement{ + cfg.PushPluginToTop(&PipelineElement{ Name: "fdsrc", Data: map[string]interface{}{ "fd": pipelineReadWriter.WriterFd(), @@ -92,9 +89,7 @@ func NewPipelineReadWriterSimpleFromConfig(cfg *PipelineConfig) (*PipelineReadWr }, }) if err := cfg.Apply(pipelineReadWriter.Pipeline()); err != nil { - if destroyErr := pipelineReadWriter.Pipeline().Destroy(); destroyErr != nil { - fmt.Println("[go-gst] Error while destroying failed pipeline instance:", destroyErr.Error()) - } + runOrPrintErr(pipelineReadWriter.Pipeline().Destroy) return nil, err } return &PipelineReadWriterSimple{pipelineReadWriter}, nil diff --git a/gst/gstauto/pipeline_simple_writer.go b/gst/gstauto/pipeline_simple_writer.go index 4e5de12..bf4f1db 100644 --- a/gst/gstauto/pipeline_simple_writer.go +++ b/gst/gstauto/pipeline_simple_writer.go @@ -25,9 +25,7 @@ func NewPipelineWriterSimpleFromString(launchStr string) (*PipelineWriterSimple, defer func() { if err != nil { - if destroyErr := pipelineWriter.Pipeline().Destroy(); destroyErr != nil { - fmt.Println("[go-gst] Error while destroying failed pipeline instance:", destroyErr.Error()) - } + runOrPrintErr(pipelineWriter.Pipeline().Destroy) } }() @@ -62,16 +60,14 @@ func NewPipelineWriterSimpleFromConfig(cfg *PipelineConfig) (*PipelineWriterSimp if err != nil { return nil, err } - cfg.pushPluginToTop(&PipelineElement{ + cfg.PushPluginToTop(&PipelineElement{ Name: "fdsrc", Data: map[string]interface{}{ "fd": pipelineWriter.WriterFd(), }, }) if err := cfg.Apply(pipelineWriter.Pipeline()); err != nil { - if destroyErr := pipelineWriter.Pipeline().Destroy(); destroyErr != nil { - fmt.Println("[go-gst] Error while destroying failed pipeline instance:", destroyErr.Error()) - } + runOrPrintErr(pipelineWriter.Pipeline().Destroy) return nil, err } return &PipelineWriterSimple{pipelineWriter}, nil diff --git a/gst/gstauto/pipeline_writer.go b/gst/gstauto/pipeline_writer.go index 11ec234..a2a68ab 100644 --- a/gst/gstauto/pipeline_writer.go +++ b/gst/gstauto/pipeline_writer.go @@ -1,8 +1,6 @@ package gstauto import ( - "fmt" - "github.com/tinyzimmer/go-gst/gst" ) @@ -25,9 +23,7 @@ func NewPipelineWriter(name string) (*PipelineWriter, error) { } wCloser, err := newWriteCloser() if err != nil { - if closeErr := pipeline.Destroy(); closeErr != nil { - fmt.Println("[gst-auto] Failed to destroy errored pipeline:", closeErr.Error()) - } + runOrPrintErr(pipeline.Destroy) return nil, err } return &PipelineWriter{ @@ -46,9 +42,7 @@ func NewPipelineWriterFromString(launchStr string) (*PipelineWriter, error) { } wCloser, err := newWriteCloser() if err != nil { - if closeErr := pipeline.Destroy(); closeErr != nil { - fmt.Println("[gst-auto] Failed to destroy errored pipeline:", closeErr.Error()) - } + runOrPrintErr(pipeline.Destroy) return nil, err } return &PipelineWriter{ diff --git a/gst/gstauto/util.go b/gst/gstauto/util.go new file mode 100644 index 0000000..0793fb3 --- /dev/null +++ b/gst/gstauto/util.go @@ -0,0 +1,9 @@ +package gstauto + +import "fmt" + +func runOrPrintErr(f func() error) { + if err := f(); err != nil { + fmt.Println("[go-gst/gst/gstauto] Internal Error:", err.Error()) + } +} diff --git a/gst/init.go b/gst/init.go new file mode 100644 index 0000000..5681ea6 --- /dev/null +++ b/gst/init.go @@ -0,0 +1,32 @@ +package gst + +// #include "gst.go.h" +import "C" +import "unsafe" + +// Init is a wrapper around gst_init() and must be called before any +// other gstreamer calls and is used to initialize everything necessary. +// In addition to setting up gstreamer for usage, a pointer to a slice of +// strings may be passed in to parse standard gst command line arguments. +// args will be modified to remove any flags that were handled. +// Alternatively, nil may be passed in to not perform any command line +// parsing. +func Init(args *[]string) { + if args != nil { + argc := C.int(len(*args)) + argv := make([]*C.char, argc) + for i, arg := range *args { + argv[i] = C.CString(arg) + } + C.gst_init((*C.int)(unsafe.Pointer(&argc)), + (***C.char)(unsafe.Pointer(&argv))) + unhandled := make([]string, argc) + for i := 0; i < int(argc); i++ { + unhandled[i] = C.GoString(argv[i]) + C.free(unsafe.Pointer(argv[i])) + } + *args = unhandled + } else { + C.gst_init(nil, nil) + } +} diff --git a/gst/pkg_config.go b/gst/pkg_config.go new file mode 100644 index 0000000..e14c2c6 --- /dev/null +++ b/gst/pkg_config.go @@ -0,0 +1,7 @@ +package gst + +/* +#cgo pkg-config: gstreamer-1.0 gstreamer-app-1.0 +#cgo CFLAGS: -Wno-deprecated-declarations -g -Wall +*/ +import "C"