diff --git a/examples/requestpad/main.go b/examples/requestpad/main.go new file mode 100644 index 0000000..f532ce0 --- /dev/null +++ b/examples/requestpad/main.go @@ -0,0 +1,159 @@ +package main + +import ( + "errors" + "fmt" + "github.com/tinyzimmer/go-glib/glib" + "github.com/tinyzimmer/go-gst/examples" + "github.com/tinyzimmer/go-gst/gst" + "os" + "time" +) + +type workflow struct { + *gst.Pipeline +} + +func (w *workflow) newSrc() { + src, err := gst.NewElementWithName("videotestsrc", "src2") + if err != nil { + fmt.Printf("err %v\n", err) + return + } + src.Set("is-live", true) + w.Add(src) + + caps, err := gst.NewElementWithName("capsfilter", "caps2") + if err != nil { + fmt.Printf("err %v\n", err) + return + } + caps.Set("caps", gst.NewCapsFromString("video/x-raw , width=640, height=360")) + w.Add(caps) + + src.Link(caps) + + // Get a sink pad on compositor + mixer, err := w.GetElementByName("mixer") + if err != nil { + fmt.Printf("err %v\n", err) + return + } + pad := mixer.GetRequestPad("sink_%u") + pad.SetProperty("xpos", 640) + pad.SetProperty("ypos", 0) + + caps.GetStaticPad("src").Link(pad) + caps.SyncStateWithParent() + src.SyncStateWithParent() + +} +func (w *workflow) delSrc() { + + mixer, err := w.GetElementByName("mixer") + if err != nil { + fmt.Printf("err %v\n", err) + return + } + + src, err := w.GetElementByName("src2") + if err != nil { + fmt.Printf("err %v\n", err) + return + } + caps, err := w.GetElementByName("caps2") + if err != nil { + fmt.Printf("err %v\n", err) + return + } + pad := mixer.GetStaticPad("sink_1") + if pad == nil { + fmt.Printf("pad is null\n") + return + } + + src.SetState(gst.StateNull) + caps.SetState(gst.StateNull) + + w.Remove(src) + w.Remove(caps) + mixer.ReleaseRequestPad(pad) +} + +func createPipeline() (*gst.Pipeline, error) { + gst.Init(nil) + var err error + var w workflow + w.Pipeline, err = gst.NewPipeline("") + if err != nil { + fmt.Println(err) + os.Exit(2) + } + elements, err := gst.NewElementMany("videotestsrc", "capsfilter", "compositor", "autovideosink") + caps := elements[1] + caps.SetProperty("caps", gst.NewCapsFromString("video/x-raw , width=640, height=360")) + caps.SetProperty("name", "caps1") + mixer := elements[2] + mixer.SetProperty("name", "mixer") + if err != nil { + fmt.Printf("err %v\n", err) + return nil, err + } + w.AddMany(elements...) + gst.ElementLinkMany(elements...) + + go func() { + time.Sleep(time.Second) + w.newSrc() + time.Sleep(time.Second) + w.delSrc() + //runtime.GC() + }() + + return w.Pipeline, nil +} + +func runPipeline(loop *glib.MainLoop, pipeline *gst.Pipeline) error { + // Start the pipeline + pipeline.SetState(gst.StatePlaying) + + // Add a message watch to the bus to quit on any error + pipeline.GetPipelineBus().AddWatch(func(msg *gst.Message) bool { + var err error + + // If the stream has ended or any element posts an error to the + // bus, populate error. + switch msg.Type() { + case gst.MessageEOS: + err = errors.New("end-of-stream") + case gst.MessageError: + // The parsed error implements the error interface, but also + // contains additional debug information. + gerr := msg.ParseError() + fmt.Println("go-gst-debug:", gerr.DebugString()) + err = gerr + } + + // If either condition triggered an error, log and quit + if err != nil { + fmt.Println("ERROR:", err.Error()) + loop.Quit() + return false + } + + return true + }) + + // Block on the main loop + return loop.RunError() +} + +func main() { + examples.RunLoop(func(loop *glib.MainLoop) error { + pipeline, err := createPipeline() + if err != nil { + return err + } + return runPipeline(loop, pipeline) + }) +} diff --git a/gst/gst_element.go b/gst/gst_element.go index 9181e22..bc3c679 100644 --- a/gst/gst_element.go +++ b/gst/gst_element.go @@ -501,3 +501,21 @@ func (e *Element) URIHandler() URIHandler { func (e *Element) RemovePad(pad *Pad) bool { return gobool(C.gst_element_remove_pad(e.Instance(), pad.Instance())) } + +// GetRequestPad gets a request pad from the element based on the name of the pad template. +// Unlike static pads, request pads are not created automatically but are only created on demand +// For example, audiomixer has sink template, 'sink_%u', which is used for creating multiple sink pads on demand so that it performs mixing of audio streams by linking multiple upstream elements on it's sink pads created on demand. +// This returns the request pad created on demand. Otherwise, it returns null if failed to create. +func (e *Element) GetRequestPad(name string) *Pad { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + pad := C.gst_element_get_request_pad(e.Instance(), (*C.gchar)(unsafe.Pointer(cname))) + if pad == nil { + return nil + } + return FromGstPadUnsafeFull(unsafe.Pointer(pad)) +} +// ReleaseRequestPad releases request pad +func (e *Element) ReleaseRequestPad(pad *Pad) { + C.gst_element_release_request_pad(e.Instance(), pad.Instance()) +}