playbin example

This commit is contained in:
tinyzimmer
2020-10-05 07:21:38 +03:00
parent 8ef82ec751
commit fd625f5572
5 changed files with 178 additions and 2 deletions

92
examples/playbin/main.go Normal file
View File

@@ -0,0 +1,92 @@
// This example demonstrates GStreamer's playbin element.
//
// This element takes an arbitrary URI as parameter, and if there is a source
// element within gstreamer, that supports this uri, the playbin will try
// to automatically create a pipeline that properly plays this media source.
// For this, the playbin internally relies on more bin elements, like the
// autovideosink and the decodebin.
// Essentially, this element is a single-element pipeline able to play
// any format from any uri-addressable source that gstreamer supports.
// Much of the playbin's behavior can be controlled by so-called flags, as well
// as the playbin's properties and signals.
package main
import (
"errors"
"fmt"
"os"
"github.com/tinyzimmer/go-gst/examples"
"github.com/tinyzimmer/go-gst/gst"
)
var srcURI string
func playbin(mainLoop *gst.MainLoop) error {
if len(os.Args) < 2 {
return errors.New("Usage: playbin <uri>")
}
gst.Init(nil)
// Create a new playbin and set the URI on it
playbin, err := gst.NewElement("playbin")
if err != nil {
return err
}
playbin.Set("uri", os.Args[1])
// The playbin element itself is a pipeline, so it can be used as one, despite being
// created from an element factory.
bus := playbin.GetBus()
playbin.SetState(gst.StatePlaying)
bus.AddWatch(func(msg *gst.Message) bool {
switch msg.Type() {
case gst.MessageEOS:
mainLoop.Quit()
return false
case gst.MessageError:
err := msg.ParseError()
fmt.Println("ERROR:", err.Error())
if debug := err.DebugString(); debug != "" {
fmt.Println("DEBUG")
fmt.Println(debug)
}
mainLoop.Quit()
return false
// Watch state change events
case gst.MessageStateChanged:
if _, newState := msg.ParseStateChanged(); newState == gst.StatePlaying {
bin := gst.BinFromElement(playbin)
// Generate a dot graph of the pipeline to GST_DEBUG_DUMP_DOT_DIR if defined
bin.DebugBinToDotFile(gst.DebugGraphShowAll, "PLAYING")
}
// Tag messages contain changes to tags on the stream. This can include metadata about
// the stream such as codecs, artists, albums, etc.
case gst.MessageTag:
tags := msg.ParseTags()
fmt.Println("Tags:")
if artist, ok := tags.GetString(gst.TagArtist); ok {
fmt.Println(" Artist:", artist)
}
if album, ok := tags.GetString(gst.TagAlbum); ok {
fmt.Println(" Album:", album)
}
if title, ok := tags.GetString(gst.TagTitle); ok {
fmt.Println(" Title:", title)
}
}
return true
})
mainLoop.Run()
return playbin.SetState(gst.StateNull)
}
func main() {
examples.RunLoop(playbin)
}

View File

@@ -196,6 +196,9 @@ const (
EventTypeCustomBothOOB EventType = C.GST_EVENT_CUSTOM_BOTH_OOB // (81923) Custom upstream or downstream out-of-band event.
)
// String implements a stringer on event types
func (e EventType) String() string { return C.GoString(C.gst_event_type_get_name(C.GstEventType(e))) }
// EventTypeFlags casts GstEventTypeFlags
type EventTypeFlags int

View File

@@ -22,6 +22,16 @@ func NewBin(name string) *Bin {
return wrapBin(&glib.Object{GObject: glib.ToGObject(unsafe.Pointer(bin))})
}
// BinFromElement wraps the given Element in a Bin reference. This only works for elements
// that implement their own Bin, such as playbin. If the provided element does not implement
// a Bin then nil is returned.
func BinFromElement(elem *Element) *Bin {
if C.toGstBin(elem.Unsafe()) == nil {
return nil
}
return &Bin{elem}
}
// Instance returns the underlying GstBin instance.
func (b *Bin) Instance() *C.GstBin { return C.toGstBin(b.Unsafe()) }
@@ -81,6 +91,15 @@ func (b *Bin) GetElementsSorted() ([]*Element, error) {
return iteratorToElementSlice(iterator)
}
// GetElementsByFactoryName returns a list of the elements in this bin created from the given factory
// name.
func (b *Bin) GetElementsByFactoryName(name string) ([]*Element, error) {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
iterator := C.gst_bin_iterate_all_by_element_factory_name(b.Instance(), (*C.gchar)(unsafe.Pointer(cname)))
return iteratorToElementSlice(iterator)
}
// Add adds an element to the bin.
func (b *Bin) Add(elem *Element) error {
if ok := C.gst_bin_add((*C.GstBin)(b.Instance()), (*C.GstElement)(elem.Instance())); !gobool(ok) {
@@ -157,6 +176,46 @@ func (b *Bin) SyncChildrenStates() bool {
return gobool(C.gst_bin_sync_children_states(b.Instance()))
}
// DEBUG OPERATIONS //
// DebugGraphDetails casts GstDebugGraphDetails
type DebugGraphDetails int
// Type castings
const (
DebugGraphShowMediaType DebugGraphDetails = C.GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE // (1) show caps-name on edges
DebugGraphShowCapsDetails DebugGraphDetails = C.GST_DEBUG_GRAPH_SHOW_CAPS_DETAILS // (2) show caps-details on edges
DebugGraphShowNonDefaultParams DebugGraphDetails = C.GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS // (4) show modified parameters on elements
DebugGraphShowStates DebugGraphDetails = C.GST_DEBUG_GRAPH_SHOW_STATES // (8) show element states
DebugGraphShowPullParams DebugGraphDetails = C.GST_DEBUG_GRAPH_SHOW_FULL_PARAMS // (16) show full element parameter values even if they are very long
DebugGraphShowAll DebugGraphDetails = C.GST_DEBUG_GRAPH_SHOW_ALL // (15) show all the typical details that one might want
DebugGraphShowVerbose DebugGraphDetails = C.GST_DEBUG_GRAPH_SHOW_VERBOSE // (4294967295) show all details regardless of how large or verbose they make the resulting output
)
// DebugBinToDotData will obtain the whole network of gstreamer elements that form the pipeline into a dot file.
// This data can be processed with graphviz to get an image.
func (b *Bin) DebugBinToDotData(details DebugGraphDetails) string {
ret := C.gst_debug_bin_to_dot_data(b.Instance(), C.GstDebugGraphDetails(details))
defer C.g_free((C.gpointer)(unsafe.Pointer(ret)))
return C.GoString(ret)
}
// DebugBinToDotFile is like DebugBinToDotData except it will write the dot data to the filename
// specified.
func (b *Bin) DebugBinToDotFile(details DebugGraphDetails, filename string) {
cname := C.CString(filename)
defer C.free(unsafe.Pointer(cname))
C.gst_debug_bin_to_dot_file(b.Instance(), C.GstDebugGraphDetails(details), (*C.gchar)(unsafe.Pointer(cname)))
}
// DebugBinToDotFileWithTs is like DebugBinToDotFile except it will write the dot data to the filename
// specified, except it will append the current timestamp to the filename.
func (b *Bin) DebugBinToDotFileWithTs(details DebugGraphDetails, filename string) {
cname := C.CString(filename)
defer C.free(unsafe.Pointer(cname))
C.gst_debug_bin_to_dot_file_with_ts(b.Instance(), C.GstDebugGraphDetails(details), (*C.gchar)(unsafe.Pointer(cname)))
}
func iteratorToElementSlice(iterator *C.GstIterator) ([]*Element, error) {
elems := make([]*Element, 0)
gval := new(C.GValue)

View File

@@ -255,13 +255,31 @@ func (e *Element) SendEvent(ev *Event) bool {
// Connect connects to the given signal on this element, and applies f as the callback. The callback must
// match the signature of the expected callback from the documentation. However, instead of specifying C types
// for arguments specify the go-gst equivalent (e.g. *gst.Element for almost all GstElement derivitives).
//
// This and the Emit() method may get moved down the hierarchy to the Object level at some point, since
func (e *Element) Connect(signal string, f interface{}) (glib.SignalHandle, error) {
// Elements are sometimes their own type unique from TYPE_ELEMENT. So make sure a type marshaler
// is registered for whatever this type is. Use the built-in element marshaler.
glib.RegisterGValueMarshalers([]glib.TypeMarshaler{{T: e.TypeFromInstance(), F: marshalElement}})
if e.TypeFromInstance() != glib.Type(C.GST_TYPE_ELEMENT) {
glib.RegisterGValueMarshalers([]glib.TypeMarshaler{{T: e.TypeFromInstance(), F: marshalElement}})
}
return e.Object.Connect(signal, f, nil)
}
// Emit is a wrapper around g_signal_emitv() and emits the signal specified by the string s to an Object. Arguments to
// callback functions connected to this signal must be specified in args. Emit() returns an interface{} which must be
// type asserted as the Go equivalent type to the return value for native C callback.
//
// Note that this code is unsafe in that the types of values in args are not checked against whether they are suitable
// for the callback.
func (e *Element) Emit(signal string, args ...interface{}) (interface{}, error) {
// We are wrapping this for the same reason as Connect.
if e.TypeFromInstance() != glib.Type(C.GST_TYPE_ELEMENT) {
glib.RegisterGValueMarshalers([]glib.TypeMarshaler{{T: e.TypeFromInstance(), F: marshalElement}})
}
return e.Object.Emit(signal, args...)
}
// SyncStateWithParent tries to change the state of the element to the same as its parent. If this function returns
// FALSE, the state of element is undefined.
func (e *Element) SyncStateWithParent() bool {

View File

@@ -47,7 +47,11 @@ func (s *Stream) StreamType() StreamType {
// Tags returns the tag list for this stream.
func (s *Stream) Tags() *TagList {
return wrapTagList(C.gst_stream_get_tags(s.Instance()))
tags := C.gst_stream_get_tags(s.Instance())
if tags == nil {
return nil
}
return wrapTagList(tags)
}
// SetCaps sets the caps for this stream.