diff --git a/examples/queries/main.go b/examples/queries/main.go new file mode 100644 index 0000000..549b904 --- /dev/null +++ b/examples/queries/main.go @@ -0,0 +1,103 @@ +// This example demonstrates how to use GStreamer's query functionality. +// +// These are a way to query information from either elements or pads. +// Such information could for example be the current position within +// the stream (i.e. the playing time). Queries can traverse the pipeline +// (both up and downstream). This functionality is essential, since most +// queries can only answered by specific elements in a pipeline (such as the +// stream's duration, which often can only be answered by the demuxer). +// Since gstreamer has many elements that itself contain other elements that +// we don't know of, we can simply send a query for the duration into the +// pipeline and the query is passed along until an element feels capable +// of answering. +// For convenience, the API has a set of pre-defined queries, but also +// allows custom queries (which can be defined and used by your own elements). +package main + +import ( + "fmt" + "os" + "strings" + "time" + + "github.com/tinyzimmer/go-gst/examples" + "github.com/tinyzimmer/go-gst/gst" +) + +func queries(mainLoop *gst.MainLoop) error { + + if len(os.Args) < 2 { + fmt.Println("USAGE: queries ") + os.Exit(1) + } + + gst.Init(nil) + + // Let GStreamer create a pipeline from the parsed launch syntax on the cli. + pipelineStr := strings.Join(os.Args[1:], " ") + pipeline, err := gst.NewPipelineFromString(pipelineStr) + if err != nil { + return err + } + + // Get a reference to the pipeline bus + bus := pipeline.GetPipelineBus() + + // Start the pipeline + pipeline.SetState(gst.StatePlaying) + + // Kick of a goroutine that will send a query to the pipeline + // every second. + go func() { + for range time.NewTicker(time.Second).C { + // Create a new position query and send it to the pipeline. + // This will traverse all elements in the pipeline, until one feels + // capable of answering the query. + pos := gst.NewPositionQuery(gst.FormatTime) + if ok := pipeline.Query(pos); !ok { + fmt.Println("Failed to query position from pipeline") + } + // Create a new duration query and send it to the pipeline. + // This will traverse all elements in the pipeline, until one feels + // capable of answering the query. + dur := gst.NewDurationQuery(gst.FormatTime) + if ok := pipeline.Query(dur); !ok { + fmt.Println("Failed to query duration from pipeline") + } + + // The values from the queries above are both in nanoseconds (this may be abstracted later). + // We can convert them to durations. + _, posVal := pos.ParsePosition() // If either of the above queries failed, these values + _, durVal := dur.ParseDuration() // will be 0. + posDur := time.Duration(posVal) * time.Nanosecond + durDur := time.Duration(durVal) * time.Nanosecond + + fmt.Println(posDur, "/", durDur) + } + }() + + bus.AddWatch(func(msg *gst.Message) bool { + switch msg.Type() { + case gst.MessageEOS: + mainLoop.Quit() + case gst.MessageError: + gstErr := msg.ParseError() + fmt.Printf("Error from %s: %s\n", msg.Source(), gstErr.Error()) + if debug := gstErr.DebugString(); debug != "" { + fmt.Println("go-gst-debug:", debug) + } + mainLoop.Quit() + } + return true + }) + + mainLoop.Run() + + bus.RemoveWatch() + + return pipeline.Destroy() +} + +func main() { + examples.RunLoop(queries) +}